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