xref: /linux/mm/cma_debug.c (revision 8325330b026509127d4541e0f511c0c10648c2d5)
128b24c1fSSasha Levin /*
228b24c1fSSasha Levin  * CMA DebugFS Interface
328b24c1fSSasha Levin  *
428b24c1fSSasha Levin  * Copyright (c) 2015 Sasha Levin <sasha.levin@oracle.com>
528b24c1fSSasha Levin  */
628b24c1fSSasha Levin 
728b24c1fSSasha Levin 
828b24c1fSSasha Levin #include <linux/debugfs.h>
928b24c1fSSasha Levin #include <linux/cma.h>
1026b02a1fSSasha Levin #include <linux/list.h>
1126b02a1fSSasha Levin #include <linux/kernel.h>
1226b02a1fSSasha Levin #include <linux/slab.h>
13*8325330bSSasha Levin #include <linux/mm_types.h>
1428b24c1fSSasha Levin 
1528b24c1fSSasha Levin #include "cma.h"
1628b24c1fSSasha Levin 
1726b02a1fSSasha Levin struct cma_mem {
1826b02a1fSSasha Levin 	struct hlist_node node;
1926b02a1fSSasha Levin 	struct page *p;
2026b02a1fSSasha Levin 	unsigned long n;
2126b02a1fSSasha Levin };
2226b02a1fSSasha Levin 
2328b24c1fSSasha Levin static struct dentry *cma_debugfs_root;
2428b24c1fSSasha Levin 
2528b24c1fSSasha Levin static int cma_debugfs_get(void *data, u64 *val)
2628b24c1fSSasha Levin {
2728b24c1fSSasha Levin 	unsigned long *p = data;
2828b24c1fSSasha Levin 
2928b24c1fSSasha Levin 	*val = *p;
3028b24c1fSSasha Levin 
3128b24c1fSSasha Levin 	return 0;
3228b24c1fSSasha Levin }
3328b24c1fSSasha Levin 
3428b24c1fSSasha Levin DEFINE_SIMPLE_ATTRIBUTE(cma_debugfs_fops, cma_debugfs_get, NULL, "%llu\n");
3528b24c1fSSasha Levin 
3626b02a1fSSasha Levin static void cma_add_to_cma_mem_list(struct cma *cma, struct cma_mem *mem)
3726b02a1fSSasha Levin {
3826b02a1fSSasha Levin 	spin_lock(&cma->mem_head_lock);
3926b02a1fSSasha Levin 	hlist_add_head(&mem->node, &cma->mem_head);
4026b02a1fSSasha Levin 	spin_unlock(&cma->mem_head_lock);
4126b02a1fSSasha Levin }
4226b02a1fSSasha Levin 
43*8325330bSSasha Levin static struct cma_mem *cma_get_entry_from_list(struct cma *cma)
44*8325330bSSasha Levin {
45*8325330bSSasha Levin 	struct cma_mem *mem = NULL;
46*8325330bSSasha Levin 
47*8325330bSSasha Levin 	spin_lock(&cma->mem_head_lock);
48*8325330bSSasha Levin 	if (!hlist_empty(&cma->mem_head)) {
49*8325330bSSasha Levin 		mem = hlist_entry(cma->mem_head.first, struct cma_mem, node);
50*8325330bSSasha Levin 		hlist_del_init(&mem->node);
51*8325330bSSasha Levin 	}
52*8325330bSSasha Levin 	spin_unlock(&cma->mem_head_lock);
53*8325330bSSasha Levin 
54*8325330bSSasha Levin 	return mem;
55*8325330bSSasha Levin }
56*8325330bSSasha Levin 
57*8325330bSSasha Levin static int cma_free_mem(struct cma *cma, int count)
58*8325330bSSasha Levin {
59*8325330bSSasha Levin 	struct cma_mem *mem = NULL;
60*8325330bSSasha Levin 
61*8325330bSSasha Levin 	while (count) {
62*8325330bSSasha Levin 		mem = cma_get_entry_from_list(cma);
63*8325330bSSasha Levin 		if (mem == NULL)
64*8325330bSSasha Levin 			return 0;
65*8325330bSSasha Levin 
66*8325330bSSasha Levin 		if (mem->n <= count) {
67*8325330bSSasha Levin 			cma_release(cma, mem->p, mem->n);
68*8325330bSSasha Levin 			count -= mem->n;
69*8325330bSSasha Levin 			kfree(mem);
70*8325330bSSasha Levin 		} else if (cma->order_per_bit == 0) {
71*8325330bSSasha Levin 			cma_release(cma, mem->p, count);
72*8325330bSSasha Levin 			mem->p += count;
73*8325330bSSasha Levin 			mem->n -= count;
74*8325330bSSasha Levin 			count = 0;
75*8325330bSSasha Levin 			cma_add_to_cma_mem_list(cma, mem);
76*8325330bSSasha Levin 		} else {
77*8325330bSSasha Levin 			pr_debug("cma: cannot release partial block when order_per_bit != 0\n");
78*8325330bSSasha Levin 			cma_add_to_cma_mem_list(cma, mem);
79*8325330bSSasha Levin 			break;
80*8325330bSSasha Levin 		}
81*8325330bSSasha Levin 	}
82*8325330bSSasha Levin 
83*8325330bSSasha Levin 	return 0;
84*8325330bSSasha Levin 
85*8325330bSSasha Levin }
86*8325330bSSasha Levin 
87*8325330bSSasha Levin static int cma_free_write(void *data, u64 val)
88*8325330bSSasha Levin {
89*8325330bSSasha Levin 	int pages = val;
90*8325330bSSasha Levin 	struct cma *cma = data;
91*8325330bSSasha Levin 
92*8325330bSSasha Levin 	return cma_free_mem(cma, pages);
93*8325330bSSasha Levin }
94*8325330bSSasha Levin 
95*8325330bSSasha Levin DEFINE_SIMPLE_ATTRIBUTE(cma_free_fops, NULL, cma_free_write, "%llu\n");
96*8325330bSSasha Levin 
9726b02a1fSSasha Levin static int cma_alloc_mem(struct cma *cma, int count)
9826b02a1fSSasha Levin {
9926b02a1fSSasha Levin 	struct cma_mem *mem;
10026b02a1fSSasha Levin 	struct page *p;
10126b02a1fSSasha Levin 
10226b02a1fSSasha Levin 	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
10326b02a1fSSasha Levin 	if (!mem)
10426b02a1fSSasha Levin 		return -ENOMEM;
10526b02a1fSSasha Levin 
10626b02a1fSSasha Levin 	p = cma_alloc(cma, count, CONFIG_CMA_ALIGNMENT);
10726b02a1fSSasha Levin 	if (!p) {
10826b02a1fSSasha Levin 		kfree(mem);
10926b02a1fSSasha Levin 		return -ENOMEM;
11026b02a1fSSasha Levin 	}
11126b02a1fSSasha Levin 
11226b02a1fSSasha Levin 	mem->p = p;
11326b02a1fSSasha Levin 	mem->n = count;
11426b02a1fSSasha Levin 
11526b02a1fSSasha Levin 	cma_add_to_cma_mem_list(cma, mem);
11626b02a1fSSasha Levin 
11726b02a1fSSasha Levin 	return 0;
11826b02a1fSSasha Levin }
11926b02a1fSSasha Levin 
12026b02a1fSSasha Levin static int cma_alloc_write(void *data, u64 val)
12126b02a1fSSasha Levin {
12226b02a1fSSasha Levin 	int pages = val;
12326b02a1fSSasha Levin 	struct cma *cma = data;
12426b02a1fSSasha Levin 
12526b02a1fSSasha Levin 	return cma_alloc_mem(cma, pages);
12626b02a1fSSasha Levin }
12726b02a1fSSasha Levin 
12826b02a1fSSasha Levin DEFINE_SIMPLE_ATTRIBUTE(cma_alloc_fops, NULL, cma_alloc_write, "%llu\n");
12926b02a1fSSasha Levin 
13028b24c1fSSasha Levin static void cma_debugfs_add_one(struct cma *cma, int idx)
13128b24c1fSSasha Levin {
13228b24c1fSSasha Levin 	struct dentry *tmp;
13328b24c1fSSasha Levin 	char name[16];
13428b24c1fSSasha Levin 	int u32s;
13528b24c1fSSasha Levin 
13628b24c1fSSasha Levin 	sprintf(name, "cma-%d", idx);
13728b24c1fSSasha Levin 
13828b24c1fSSasha Levin 	tmp = debugfs_create_dir(name, cma_debugfs_root);
13928b24c1fSSasha Levin 
14026b02a1fSSasha Levin 	debugfs_create_file("alloc", S_IWUSR, cma_debugfs_root, cma,
14126b02a1fSSasha Levin 				&cma_alloc_fops);
14226b02a1fSSasha Levin 
143*8325330bSSasha Levin 	debugfs_create_file("free", S_IWUSR, cma_debugfs_root, cma,
144*8325330bSSasha Levin 				&cma_free_fops);
145*8325330bSSasha Levin 
14628b24c1fSSasha Levin 	debugfs_create_file("base_pfn", S_IRUGO, tmp,
14728b24c1fSSasha Levin 				&cma->base_pfn, &cma_debugfs_fops);
14828b24c1fSSasha Levin 	debugfs_create_file("count", S_IRUGO, tmp,
14928b24c1fSSasha Levin 				&cma->count, &cma_debugfs_fops);
15028b24c1fSSasha Levin 	debugfs_create_file("order_per_bit", S_IRUGO, tmp,
15128b24c1fSSasha Levin 				&cma->order_per_bit, &cma_debugfs_fops);
15228b24c1fSSasha Levin 
15328b24c1fSSasha Levin 	u32s = DIV_ROUND_UP(cma_bitmap_maxno(cma), BITS_PER_BYTE * sizeof(u32));
15428b24c1fSSasha Levin 	debugfs_create_u32_array("bitmap", S_IRUGO, tmp, (u32*)cma->bitmap, u32s);
15528b24c1fSSasha Levin }
15628b24c1fSSasha Levin 
15728b24c1fSSasha Levin static int __init cma_debugfs_init(void)
15828b24c1fSSasha Levin {
15928b24c1fSSasha Levin 	int i;
16028b24c1fSSasha Levin 
16128b24c1fSSasha Levin 	cma_debugfs_root = debugfs_create_dir("cma", NULL);
16228b24c1fSSasha Levin 	if (!cma_debugfs_root)
16328b24c1fSSasha Levin 		return -ENOMEM;
16428b24c1fSSasha Levin 
16528b24c1fSSasha Levin 	for (i = 0; i < cma_area_count; i++)
16628b24c1fSSasha Levin 		cma_debugfs_add_one(&cma_areas[i], i);
16728b24c1fSSasha Levin 
16828b24c1fSSasha Levin 	return 0;
16928b24c1fSSasha Levin }
17028b24c1fSSasha Levin late_initcall(cma_debugfs_init);
171