xref: /OK3568_Linux_fs/kernel/drivers/base/arm/memory_group_manager/memory_group_manager.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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