1 // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
2 /*
3 *
4 * (C) COPYRIGHT 2019-2022 ARM Limited. All rights reserved.
5 *
6 * This program is free software and is provided to you under the terms of the
7 * GNU General Public License version 2 as published by the Free Software
8 * Foundation, and any use by you of this program is subject to the terms
9 * of such GNU license.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you can access it online at
18 * http://www.gnu.org/licenses/gpl-2.0.html.
19 *
20 */
21
22 #include <linux/fs.h>
23 #include <linux/of.h>
24 #include <linux/slab.h>
25 #include <linux/platform_device.h>
26 #include <linux/version.h>
27 #include <linux/module.h>
28 #if IS_ENABLED(CONFIG_DEBUG_FS)
29 #include <linux/debugfs.h>
30 #include <linux/version_compat_defs.h>
31 #endif
32 #include <linux/mm.h>
33 #include <linux/memory_group_manager.h>
34
35 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
36 #undef DEFINE_SIMPLE_ATTRIBUTE
37 #define DEFINE_SIMPLE_ATTRIBUTE DEFINE_DEBUGFS_ATTRIBUTE
38 #define debugfs_create_file debugfs_create_file_unsafe
39 #endif
40
41 #if (KERNEL_VERSION(4, 20, 0) > LINUX_VERSION_CODE)
vmf_insert_pfn_prot(struct vm_area_struct * vma,unsigned long addr,unsigned long pfn,pgprot_t pgprot)42 static inline vm_fault_t vmf_insert_pfn_prot(struct vm_area_struct *vma,
43 unsigned long addr, unsigned long pfn, pgprot_t pgprot)
44 {
45 int err = vm_insert_pfn_prot(vma, addr, pfn, pgprot);
46
47 if (unlikely(err == -ENOMEM))
48 return VM_FAULT_OOM;
49 if (unlikely(err < 0 && err != -EBUSY))
50 return VM_FAULT_SIGBUS;
51
52 return VM_FAULT_NOPAGE;
53 }
54 #endif
55
56 #define PTE_PBHA_SHIFT (59)
57 #define PTE_PBHA_MASK ((uint64_t)0xf << PTE_PBHA_SHIFT)
58 #define PTE_RES_BIT_MULTI_AS_SHIFT (63)
59
60 #define IMPORTED_MEMORY_ID (MEMORY_GROUP_MANAGER_NR_GROUPS - 1)
61
62 /**
63 * struct mgm_group - Structure to keep track of the number of allocated
64 * pages per group
65 *
66 * @size: The number of allocated small(4KB) pages
67 * @lp_size: The number of allocated large(2MB) pages
68 * @insert_pfn: The number of calls to map pages for CPU access.
69 * @update_gpu_pte: The number of calls to update GPU page table entries.
70 *
71 * This structure allows page allocation information to be displayed via
72 * debugfs. Display is organized per group with small and large sized pages.
73 */
74 struct mgm_group {
75 size_t size;
76 size_t lp_size;
77 size_t insert_pfn;
78 size_t update_gpu_pte;
79 };
80
81 /**
82 * struct mgm_groups - Structure for groups of memory group manager
83 *
84 * @groups: To keep track of the number of allocated pages of all groups
85 * @dev: device attached
86 * @mgm_debugfs_root: debugfs root directory of memory group manager
87 *
88 * This structure allows page allocation information to be displayed via
89 * debugfs. Display is organized per group with small and large sized pages.
90 */
91 struct mgm_groups {
92 struct mgm_group groups[MEMORY_GROUP_MANAGER_NR_GROUPS];
93 struct device *dev;
94 #if IS_ENABLED(CONFIG_DEBUG_FS)
95 struct dentry *mgm_debugfs_root;
96 #endif
97 };
98
99 #if IS_ENABLED(CONFIG_DEBUG_FS)
100
mgm_size_get(void * data,u64 * val)101 static int mgm_size_get(void *data, u64 *val)
102 {
103 struct mgm_group *group = data;
104
105 *val = group->size;
106
107 return 0;
108 }
109
mgm_lp_size_get(void * data,u64 * val)110 static int mgm_lp_size_get(void *data, u64 *val)
111 {
112 struct mgm_group *group = data;
113
114 *val = group->lp_size;
115
116 return 0;
117 }
118
mgm_insert_pfn_get(void * data,u64 * val)119 static int mgm_insert_pfn_get(void *data, u64 *val)
120 {
121 struct mgm_group *group = data;
122
123 *val = group->insert_pfn;
124
125 return 0;
126 }
127
mgm_update_gpu_pte_get(void * data,u64 * val)128 static int mgm_update_gpu_pte_get(void *data, u64 *val)
129 {
130 struct mgm_group *group = data;
131
132 *val = group->update_gpu_pte;
133
134 return 0;
135 }
136
137 DEFINE_DEBUGFS_ATTRIBUTE(fops_mgm_size, mgm_size_get, NULL, "%llu\n");
138 DEFINE_DEBUGFS_ATTRIBUTE(fops_mgm_lp_size, mgm_lp_size_get, NULL, "%llu\n");
139 DEFINE_DEBUGFS_ATTRIBUTE(fops_mgm_insert_pfn, mgm_insert_pfn_get, NULL, "%llu\n");
140 DEFINE_DEBUGFS_ATTRIBUTE(fops_mgm_update_gpu_pte, mgm_update_gpu_pte_get, NULL, "%llu\n");
141
mgm_term_debugfs(struct mgm_groups * data)142 static void mgm_term_debugfs(struct mgm_groups *data)
143 {
144 debugfs_remove_recursive(data->mgm_debugfs_root);
145 }
146
147 #define MGM_DEBUGFS_GROUP_NAME_MAX 10
mgm_initialize_debugfs(struct mgm_groups * mgm_data)148 static int mgm_initialize_debugfs(struct mgm_groups *mgm_data)
149 {
150 int i;
151 struct dentry *e, *g;
152 char debugfs_group_name[MGM_DEBUGFS_GROUP_NAME_MAX];
153
154 /*
155 * Create root directory of memory-group-manager
156 */
157 mgm_data->mgm_debugfs_root =
158 debugfs_create_dir("physical-memory-group-manager", NULL);
159 if (IS_ERR_OR_NULL(mgm_data->mgm_debugfs_root)) {
160 dev_err(mgm_data->dev, "fail to create debugfs root directory\n");
161 return -ENODEV;
162 }
163
164 /*
165 * Create debugfs files per group
166 */
167 for (i = 0; i < MEMORY_GROUP_MANAGER_NR_GROUPS; i++) {
168 scnprintf(debugfs_group_name, MGM_DEBUGFS_GROUP_NAME_MAX,
169 "group_%d", i);
170 g = debugfs_create_dir(debugfs_group_name,
171 mgm_data->mgm_debugfs_root);
172 if (IS_ERR_OR_NULL(g)) {
173 dev_err(mgm_data->dev, "fail to create group[%d]\n", i);
174 goto remove_debugfs;
175 }
176
177 e = debugfs_create_file("size", 0444, g, &mgm_data->groups[i],
178 &fops_mgm_size);
179 if (IS_ERR_OR_NULL(e)) {
180 dev_err(mgm_data->dev, "fail to create size[%d]\n", i);
181 goto remove_debugfs;
182 }
183
184 e = debugfs_create_file("lp_size", 0444, g,
185 &mgm_data->groups[i], &fops_mgm_lp_size);
186 if (IS_ERR_OR_NULL(e)) {
187 dev_err(mgm_data->dev,
188 "fail to create lp_size[%d]\n", i);
189 goto remove_debugfs;
190 }
191
192 e = debugfs_create_file("insert_pfn", 0444, g,
193 &mgm_data->groups[i], &fops_mgm_insert_pfn);
194 if (IS_ERR_OR_NULL(e)) {
195 dev_err(mgm_data->dev,
196 "fail to create insert_pfn[%d]\n", i);
197 goto remove_debugfs;
198 }
199
200 e = debugfs_create_file("update_gpu_pte", 0444, g,
201 &mgm_data->groups[i], &fops_mgm_update_gpu_pte);
202 if (IS_ERR_OR_NULL(e)) {
203 dev_err(mgm_data->dev,
204 "fail to create update_gpu_pte[%d]\n", i);
205 goto remove_debugfs;
206 }
207 }
208
209 return 0;
210
211 remove_debugfs:
212 mgm_term_debugfs(mgm_data);
213 return -ENODEV;
214 }
215
216 #else
217
mgm_term_debugfs(struct mgm_groups * data)218 static void mgm_term_debugfs(struct mgm_groups *data)
219 {
220 }
221
mgm_initialize_debugfs(struct mgm_groups * mgm_data)222 static int mgm_initialize_debugfs(struct mgm_groups *mgm_data)
223 {
224 return 0;
225 }
226
227 #endif /* CONFIG_DEBUG_FS */
228
229 #define ORDER_SMALL_PAGE 0
230 #define ORDER_LARGE_PAGE 9
update_size(struct memory_group_manager_device * mgm_dev,unsigned int group_id,int order,bool alloc)231 static void update_size(struct memory_group_manager_device *mgm_dev, unsigned int group_id,
232 int order, bool alloc)
233 {
234 struct mgm_groups *data = mgm_dev->data;
235
236 switch (order) {
237 case ORDER_SMALL_PAGE:
238 if (alloc)
239 data->groups[group_id].size++;
240 else {
241 WARN_ON(data->groups[group_id].size == 0);
242 data->groups[group_id].size--;
243 }
244 break;
245
246 case ORDER_LARGE_PAGE:
247 if (alloc)
248 data->groups[group_id].lp_size++;
249 else {
250 WARN_ON(data->groups[group_id].lp_size == 0);
251 data->groups[group_id].lp_size--;
252 }
253 break;
254
255 default:
256 dev_err(data->dev, "Unknown order(%d)\n", order);
257 break;
258 }
259 }
260
example_mgm_alloc_page(struct memory_group_manager_device * mgm_dev,int group_id,gfp_t gfp_mask,unsigned int order)261 static struct page *example_mgm_alloc_page(
262 struct memory_group_manager_device *mgm_dev, int group_id,
263 gfp_t gfp_mask, unsigned int order)
264 {
265 struct mgm_groups *const data = mgm_dev->data;
266 struct page *p;
267
268 dev_dbg(data->dev, "%s(mgm_dev=%pK, group_id=%d gfp_mask=0x%x order=%u\n", __func__,
269 (void *)mgm_dev, group_id, gfp_mask, order);
270
271 if (WARN_ON(group_id < 0) ||
272 WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
273 return NULL;
274
275 p = alloc_pages(gfp_mask, order);
276
277 if (p) {
278 update_size(mgm_dev, group_id, order, true);
279 } else {
280 struct mgm_groups *data = mgm_dev->data;
281
282 dev_err(data->dev, "alloc_pages failed\n");
283 }
284
285 return p;
286 }
287
example_mgm_free_page(struct memory_group_manager_device * mgm_dev,int group_id,struct page * page,unsigned int order)288 static void example_mgm_free_page(
289 struct memory_group_manager_device *mgm_dev, int group_id,
290 struct page *page, unsigned int order)
291 {
292 struct mgm_groups *const data = mgm_dev->data;
293
294 dev_dbg(data->dev, "%s(mgm_dev=%pK, group_id=%d page=%pK order=%u\n", __func__,
295 (void *)mgm_dev, group_id, (void *)page, order);
296
297 if (WARN_ON(group_id < 0) ||
298 WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
299 return;
300
301 __free_pages(page, order);
302
303 update_size(mgm_dev, group_id, order, false);
304 }
305
example_mgm_get_import_memory_id(struct memory_group_manager_device * mgm_dev,struct memory_group_manager_import_data * import_data)306 static int example_mgm_get_import_memory_id(
307 struct memory_group_manager_device *mgm_dev,
308 struct memory_group_manager_import_data *import_data)
309 {
310 struct mgm_groups *const data = mgm_dev->data;
311
312 dev_dbg(data->dev, "%s(mgm_dev=%pK, import_data=%pK (type=%d)\n", __func__, (void *)mgm_dev,
313 (void *)import_data, (int)import_data->type);
314
315 if (!WARN_ON(!import_data)) {
316 WARN_ON(!import_data->u.dma_buf);
317
318 WARN_ON(import_data->type !=
319 MEMORY_GROUP_MANAGER_IMPORT_TYPE_DMA_BUF);
320 }
321
322 return IMPORTED_MEMORY_ID;
323 }
324
example_mgm_update_gpu_pte(struct memory_group_manager_device * const mgm_dev,int const group_id,int const mmu_level,u64 pte)325 static u64 example_mgm_update_gpu_pte(
326 struct memory_group_manager_device *const mgm_dev, int const group_id,
327 int const mmu_level, u64 pte)
328 {
329 struct mgm_groups *const data = mgm_dev->data;
330
331 dev_dbg(data->dev, "%s(mgm_dev=%pK, group_id=%d, mmu_level=%d, pte=0x%llx)\n", __func__,
332 (void *)mgm_dev, group_id, mmu_level, pte);
333
334 if (WARN_ON(group_id < 0) ||
335 WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
336 return pte;
337
338 pte |= ((u64)group_id << PTE_PBHA_SHIFT) & PTE_PBHA_MASK;
339
340 /* Address could be translated into a different bus address here */
341 pte |= ((u64)1 << PTE_RES_BIT_MULTI_AS_SHIFT);
342
343 data->groups[group_id].update_gpu_pte++;
344
345 return pte;
346 }
347
example_mgm_pte_to_original_pte(struct memory_group_manager_device * const mgm_dev,int const group_id,int const mmu_level,u64 pte)348 static u64 example_mgm_pte_to_original_pte(struct memory_group_manager_device *const mgm_dev,
349 int const group_id, int const mmu_level, u64 pte)
350 {
351 /* Undo the group ID modification */
352 pte &= ~PTE_PBHA_MASK;
353 /* Undo the bit set */
354 pte &= ~((u64)1 << PTE_RES_BIT_MULTI_AS_SHIFT);
355
356 return pte;
357 }
358
example_mgm_vmf_insert_pfn_prot(struct memory_group_manager_device * const mgm_dev,int const group_id,struct vm_area_struct * const vma,unsigned long const addr,unsigned long const pfn,pgprot_t const prot)359 static vm_fault_t example_mgm_vmf_insert_pfn_prot(
360 struct memory_group_manager_device *const mgm_dev, int const group_id,
361 struct vm_area_struct *const vma, unsigned long const addr,
362 unsigned long const pfn, pgprot_t const prot)
363 {
364 struct mgm_groups *const data = mgm_dev->data;
365 vm_fault_t fault;
366
367 dev_dbg(data->dev,
368 "%s(mgm_dev=%pK, group_id=%d, vma=%pK, addr=0x%lx, pfn=0x%lx, prot=0x%llx)\n",
369 __func__, (void *)mgm_dev, group_id, (void *)vma, addr, pfn,
370 (unsigned long long)pgprot_val(prot));
371
372 if (WARN_ON(group_id < 0) ||
373 WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
374 return VM_FAULT_SIGBUS;
375
376 fault = vmf_insert_pfn_prot(vma, addr, pfn, prot);
377
378 if (fault == VM_FAULT_NOPAGE)
379 data->groups[group_id].insert_pfn++;
380 else
381 dev_err(data->dev, "vmf_insert_pfn_prot failed\n");
382
383 return fault;
384 }
385
mgm_initialize_data(struct mgm_groups * mgm_data)386 static int mgm_initialize_data(struct mgm_groups *mgm_data)
387 {
388 int i;
389
390 for (i = 0; i < MEMORY_GROUP_MANAGER_NR_GROUPS; i++) {
391 mgm_data->groups[i].size = 0;
392 mgm_data->groups[i].lp_size = 0;
393 mgm_data->groups[i].insert_pfn = 0;
394 mgm_data->groups[i].update_gpu_pte = 0;
395 }
396
397 return mgm_initialize_debugfs(mgm_data);
398 }
399
mgm_term_data(struct mgm_groups * data)400 static void mgm_term_data(struct mgm_groups *data)
401 {
402 int i;
403
404 for (i = 0; i < MEMORY_GROUP_MANAGER_NR_GROUPS; i++) {
405 if (data->groups[i].size != 0)
406 dev_warn(data->dev,
407 "%zu 0-order pages in group(%d) leaked\n",
408 data->groups[i].size, i);
409 if (data->groups[i].lp_size != 0)
410 dev_warn(data->dev,
411 "%zu 9 order pages in group(%d) leaked\n",
412 data->groups[i].lp_size, i);
413 }
414
415 mgm_term_debugfs(data);
416 }
417
memory_group_manager_probe(struct platform_device * pdev)418 static int memory_group_manager_probe(struct platform_device *pdev)
419 {
420 struct memory_group_manager_device *mgm_dev;
421 struct mgm_groups *mgm_data;
422
423 mgm_dev = kzalloc(sizeof(*mgm_dev), GFP_KERNEL);
424 if (!mgm_dev)
425 return -ENOMEM;
426
427 mgm_dev->owner = THIS_MODULE;
428 mgm_dev->ops.mgm_alloc_page = example_mgm_alloc_page;
429 mgm_dev->ops.mgm_free_page = example_mgm_free_page;
430 mgm_dev->ops.mgm_get_import_memory_id =
431 example_mgm_get_import_memory_id;
432 mgm_dev->ops.mgm_vmf_insert_pfn_prot = example_mgm_vmf_insert_pfn_prot;
433 mgm_dev->ops.mgm_update_gpu_pte = example_mgm_update_gpu_pte;
434 mgm_dev->ops.mgm_pte_to_original_pte = example_mgm_pte_to_original_pte;
435
436 mgm_data = kzalloc(sizeof(*mgm_data), GFP_KERNEL);
437 if (!mgm_data) {
438 kfree(mgm_dev);
439 return -ENOMEM;
440 }
441
442 mgm_dev->data = mgm_data;
443 mgm_data->dev = &pdev->dev;
444
445 if (mgm_initialize_data(mgm_data)) {
446 kfree(mgm_data);
447 kfree(mgm_dev);
448 return -ENOENT;
449 }
450
451 platform_set_drvdata(pdev, mgm_dev);
452 dev_info(&pdev->dev, "Memory group manager probed successfully\n");
453
454 return 0;
455 }
456
memory_group_manager_remove(struct platform_device * pdev)457 static int memory_group_manager_remove(struct platform_device *pdev)
458 {
459 struct memory_group_manager_device *mgm_dev =
460 platform_get_drvdata(pdev);
461 struct mgm_groups *mgm_data = mgm_dev->data;
462
463 mgm_term_data(mgm_data);
464 kfree(mgm_data);
465
466 kfree(mgm_dev);
467
468 dev_info(&pdev->dev, "Memory group manager removed successfully\n");
469
470 return 0;
471 }
472
473 static const struct of_device_id memory_group_manager_dt_ids[] = {
474 { .compatible = "arm,physical-memory-group-manager" },
475 { /* sentinel */ }
476 };
477 MODULE_DEVICE_TABLE(of, memory_group_manager_dt_ids);
478
479 static struct platform_driver memory_group_manager_driver = {
480 .probe = memory_group_manager_probe,
481 .remove = memory_group_manager_remove,
482 .driver = {
483 .name = "physical-memory-group-manager",
484 .of_match_table = of_match_ptr(memory_group_manager_dt_ids),
485 /*
486 * Prevent the mgm_dev from being unbound and freed, as other's
487 * may have pointers to it and would get confused, or crash, if
488 * it suddenly disappear.
489 */
490 .suppress_bind_attrs = true,
491 }
492 };
493
494 module_platform_driver(memory_group_manager_driver);
495
496 MODULE_LICENSE("GPL");
497 MODULE_AUTHOR("ARM Ltd.");
498 MODULE_VERSION("1.0");
499