18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CMA DebugFS Interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Sasha Levin <sasha.levin@oracle.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 108c2ecf20Sopenharmony_ci#include <linux/cma.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "cma.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct cma_mem { 198c2ecf20Sopenharmony_ci struct hlist_node node; 208c2ecf20Sopenharmony_ci struct page *p; 218c2ecf20Sopenharmony_ci unsigned long n; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int cma_debugfs_get(void *data, u64 *val) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci unsigned long *p = data; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci *val = *p; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci return 0; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(cma_debugfs_fops, cma_debugfs_get, NULL, "%llu\n"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int cma_used_get(void *data, u64 *val) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct cma *cma = data; 378c2ecf20Sopenharmony_ci unsigned long used; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci mutex_lock(&cma->lock); 408c2ecf20Sopenharmony_ci /* pages counter is smaller than sizeof(int) */ 418c2ecf20Sopenharmony_ci used = bitmap_weight(cma->bitmap, (int)cma_bitmap_maxno(cma)); 428c2ecf20Sopenharmony_ci mutex_unlock(&cma->lock); 438c2ecf20Sopenharmony_ci *val = (u64)used << cma->order_per_bit; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(cma_used_fops, cma_used_get, NULL, "%llu\n"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int cma_maxchunk_get(void *data, u64 *val) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct cma *cma = data; 528c2ecf20Sopenharmony_ci unsigned long maxchunk = 0; 538c2ecf20Sopenharmony_ci unsigned long start, end = 0; 548c2ecf20Sopenharmony_ci unsigned long bitmap_maxno = cma_bitmap_maxno(cma); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci mutex_lock(&cma->lock); 578c2ecf20Sopenharmony_ci for (;;) { 588c2ecf20Sopenharmony_ci start = find_next_zero_bit(cma->bitmap, bitmap_maxno, end); 598c2ecf20Sopenharmony_ci if (start >= bitmap_maxno) 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci end = find_next_bit(cma->bitmap, bitmap_maxno, start); 628c2ecf20Sopenharmony_ci maxchunk = max(end - start, maxchunk); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci mutex_unlock(&cma->lock); 658c2ecf20Sopenharmony_ci *val = (u64)maxchunk << cma->order_per_bit; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(cma_maxchunk_fops, cma_maxchunk_get, NULL, "%llu\n"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void cma_add_to_cma_mem_list(struct cma *cma, struct cma_mem *mem) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci spin_lock(&cma->mem_head_lock); 748c2ecf20Sopenharmony_ci hlist_add_head(&mem->node, &cma->mem_head); 758c2ecf20Sopenharmony_ci spin_unlock(&cma->mem_head_lock); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic struct cma_mem *cma_get_entry_from_list(struct cma *cma) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct cma_mem *mem = NULL; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci spin_lock(&cma->mem_head_lock); 838c2ecf20Sopenharmony_ci if (!hlist_empty(&cma->mem_head)) { 848c2ecf20Sopenharmony_ci mem = hlist_entry(cma->mem_head.first, struct cma_mem, node); 858c2ecf20Sopenharmony_ci hlist_del_init(&mem->node); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci spin_unlock(&cma->mem_head_lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return mem; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int cma_free_mem(struct cma *cma, int count) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct cma_mem *mem = NULL; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci while (count) { 978c2ecf20Sopenharmony_ci mem = cma_get_entry_from_list(cma); 988c2ecf20Sopenharmony_ci if (mem == NULL) 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (mem->n <= count) { 1028c2ecf20Sopenharmony_ci cma_release(cma, mem->p, mem->n); 1038c2ecf20Sopenharmony_ci count -= mem->n; 1048c2ecf20Sopenharmony_ci kfree(mem); 1058c2ecf20Sopenharmony_ci } else if (cma->order_per_bit == 0) { 1068c2ecf20Sopenharmony_ci cma_release(cma, mem->p, count); 1078c2ecf20Sopenharmony_ci mem->p += count; 1088c2ecf20Sopenharmony_ci mem->n -= count; 1098c2ecf20Sopenharmony_ci count = 0; 1108c2ecf20Sopenharmony_ci cma_add_to_cma_mem_list(cma, mem); 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci pr_debug("cma: cannot release partial block when order_per_bit != 0\n"); 1138c2ecf20Sopenharmony_ci cma_add_to_cma_mem_list(cma, mem); 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int cma_free_write(void *data, u64 val) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int pages = val; 1258c2ecf20Sopenharmony_ci struct cma *cma = data; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return cma_free_mem(cma, pages); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(cma_free_fops, NULL, cma_free_write, "%llu\n"); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int cma_alloc_mem(struct cma *cma, int count) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct cma_mem *mem; 1348c2ecf20Sopenharmony_ci struct page *p; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci mem = kzalloc(sizeof(*mem), GFP_KERNEL); 1378c2ecf20Sopenharmony_ci if (!mem) 1388c2ecf20Sopenharmony_ci return -ENOMEM; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci p = cma_alloc(cma, count, 0, false); 1418c2ecf20Sopenharmony_ci if (!p) { 1428c2ecf20Sopenharmony_ci kfree(mem); 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mem->p = p; 1478c2ecf20Sopenharmony_ci mem->n = count; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci cma_add_to_cma_mem_list(cma, mem); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int cma_alloc_write(void *data, u64 val) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int pages = val; 1578c2ecf20Sopenharmony_ci struct cma *cma = data; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return cma_alloc_mem(cma, pages); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(cma_alloc_fops, NULL, cma_alloc_write, "%llu\n"); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void cma_debugfs_add_one(struct cma *cma, struct dentry *root_dentry) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct dentry *tmp; 1668c2ecf20Sopenharmony_ci char name[16]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci scnprintf(name, sizeof(name), "cma-%s", cma->name); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci tmp = debugfs_create_dir(name, root_dentry); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci debugfs_create_file("alloc", 0200, tmp, cma, &cma_alloc_fops); 1738c2ecf20Sopenharmony_ci debugfs_create_file("free", 0200, tmp, cma, &cma_free_fops); 1748c2ecf20Sopenharmony_ci debugfs_create_file("base_pfn", 0444, tmp, 1758c2ecf20Sopenharmony_ci &cma->base_pfn, &cma_debugfs_fops); 1768c2ecf20Sopenharmony_ci debugfs_create_file("count", 0444, tmp, &cma->count, &cma_debugfs_fops); 1778c2ecf20Sopenharmony_ci debugfs_create_file("order_per_bit", 0444, tmp, 1788c2ecf20Sopenharmony_ci &cma->order_per_bit, &cma_debugfs_fops); 1798c2ecf20Sopenharmony_ci debugfs_create_file("used", 0444, tmp, cma, &cma_used_fops); 1808c2ecf20Sopenharmony_ci debugfs_create_file("maxchunk", 0444, tmp, cma, &cma_maxchunk_fops); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci cma->dfs_bitmap.array = (u32 *)cma->bitmap; 1838c2ecf20Sopenharmony_ci cma->dfs_bitmap.n_elements = DIV_ROUND_UP(cma_bitmap_maxno(cma), 1848c2ecf20Sopenharmony_ci BITS_PER_BYTE * sizeof(u32)); 1858c2ecf20Sopenharmony_ci debugfs_create_u32_array("bitmap", 0444, tmp, &cma->dfs_bitmap); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int __init cma_debugfs_init(void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct dentry *cma_debugfs_root; 1918c2ecf20Sopenharmony_ci int i; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci cma_debugfs_root = debugfs_create_dir("cma", NULL); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci for (i = 0; i < cma_area_count; i++) 1968c2ecf20Sopenharmony_ci cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_cilate_initcall(cma_debugfs_init); 201