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