blob: 00170c41cf81bed949575ad5f99a6f8e3d5fcd1f [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Sasha Levin28b24c12015-04-14 15:44:57 -07002/*
3 * CMA DebugFS Interface
4 *
5 * Copyright (c) 2015 Sasha Levin <sasha.levin@oracle.com>
6 */
7
8
9#include <linux/debugfs.h>
10#include <linux/cma.h>
Sasha Levin26b02a12015-04-14 15:44:59 -070011#include <linux/list.h>
12#include <linux/kernel.h>
13#include <linux/slab.h>
Sasha Levin83253302015-04-14 15:45:02 -070014#include <linux/mm_types.h>
Sasha Levin28b24c12015-04-14 15:44:57 -070015
16#include "cma.h"
17
Sasha Levin26b02a12015-04-14 15:44:59 -070018struct cma_mem {
19 struct hlist_node node;
20 struct page *p;
21 unsigned long n;
22};
23
Sasha Levin28b24c12015-04-14 15:44:57 -070024static int cma_debugfs_get(void *data, u64 *val)
25{
26 unsigned long *p = data;
27
28 *val = *p;
29
30 return 0;
31}
zhong jianga9ea2422019-11-30 17:57:25 -080032DEFINE_DEBUGFS_ATTRIBUTE(cma_debugfs_fops, cma_debugfs_get, NULL, "%llu\n");
Sasha Levin28b24c12015-04-14 15:44:57 -070033
Dmitry Safonov2e32b942015-04-15 16:14:59 -070034static int cma_used_get(void *data, u64 *val)
35{
36 struct cma *cma = data;
37 unsigned long used;
38
39 mutex_lock(&cma->lock);
40 /* pages counter is smaller than sizeof(int) */
Joonsoo Kimd56e84b2015-07-17 16:24:23 -070041 used = bitmap_weight(cma->bitmap, (int)cma_bitmap_maxno(cma));
Dmitry Safonov2e32b942015-04-15 16:14:59 -070042 mutex_unlock(&cma->lock);
43 *val = (u64)used << cma->order_per_bit;
44
45 return 0;
46}
zhong jianga9ea2422019-11-30 17:57:25 -080047DEFINE_DEBUGFS_ATTRIBUTE(cma_used_fops, cma_used_get, NULL, "%llu\n");
Dmitry Safonov2e32b942015-04-15 16:14:59 -070048
49static int cma_maxchunk_get(void *data, u64 *val)
50{
51 struct cma *cma = data;
52 unsigned long maxchunk = 0;
53 unsigned long start, end = 0;
Joonsoo Kimd56e84b2015-07-17 16:24:23 -070054 unsigned long bitmap_maxno = cma_bitmap_maxno(cma);
Dmitry Safonov2e32b942015-04-15 16:14:59 -070055
56 mutex_lock(&cma->lock);
57 for (;;) {
Joonsoo Kimd56e84b2015-07-17 16:24:23 -070058 start = find_next_zero_bit(cma->bitmap, bitmap_maxno, end);
Yue Huf0fd5052019-05-13 17:16:37 -070059 if (start >= bitmap_maxno)
Dmitry Safonov2e32b942015-04-15 16:14:59 -070060 break;
Joonsoo Kimd56e84b2015-07-17 16:24:23 -070061 end = find_next_bit(cma->bitmap, bitmap_maxno, start);
Dmitry Safonov2e32b942015-04-15 16:14:59 -070062 maxchunk = max(end - start, maxchunk);
63 }
64 mutex_unlock(&cma->lock);
65 *val = (u64)maxchunk << cma->order_per_bit;
66
67 return 0;
68}
zhong jianga9ea2422019-11-30 17:57:25 -080069DEFINE_DEBUGFS_ATTRIBUTE(cma_maxchunk_fops, cma_maxchunk_get, NULL, "%llu\n");
Dmitry Safonov2e32b942015-04-15 16:14:59 -070070
Sasha Levin26b02a12015-04-14 15:44:59 -070071static void cma_add_to_cma_mem_list(struct cma *cma, struct cma_mem *mem)
72{
73 spin_lock(&cma->mem_head_lock);
74 hlist_add_head(&mem->node, &cma->mem_head);
75 spin_unlock(&cma->mem_head_lock);
76}
77
Sasha Levin83253302015-04-14 15:45:02 -070078static struct cma_mem *cma_get_entry_from_list(struct cma *cma)
79{
80 struct cma_mem *mem = NULL;
81
82 spin_lock(&cma->mem_head_lock);
83 if (!hlist_empty(&cma->mem_head)) {
84 mem = hlist_entry(cma->mem_head.first, struct cma_mem, node);
85 hlist_del_init(&mem->node);
86 }
87 spin_unlock(&cma->mem_head_lock);
88
89 return mem;
90}
91
92static int cma_free_mem(struct cma *cma, int count)
93{
94 struct cma_mem *mem = NULL;
95
96 while (count) {
97 mem = cma_get_entry_from_list(cma);
98 if (mem == NULL)
99 return 0;
100
101 if (mem->n <= count) {
102 cma_release(cma, mem->p, mem->n);
103 count -= mem->n;
104 kfree(mem);
105 } else if (cma->order_per_bit == 0) {
106 cma_release(cma, mem->p, count);
107 mem->p += count;
108 mem->n -= count;
109 count = 0;
110 cma_add_to_cma_mem_list(cma, mem);
111 } else {
112 pr_debug("cma: cannot release partial block when order_per_bit != 0\n");
113 cma_add_to_cma_mem_list(cma, mem);
114 break;
115 }
116 }
117
118 return 0;
119
120}
121
122static int cma_free_write(void *data, u64 val)
123{
124 int pages = val;
125 struct cma *cma = data;
126
127 return cma_free_mem(cma, pages);
128}
zhong jianga9ea2422019-11-30 17:57:25 -0800129DEFINE_DEBUGFS_ATTRIBUTE(cma_free_fops, NULL, cma_free_write, "%llu\n");
Sasha Levin83253302015-04-14 15:45:02 -0700130
Sasha Levin26b02a12015-04-14 15:44:59 -0700131static int cma_alloc_mem(struct cma *cma, int count)
132{
133 struct cma_mem *mem;
134 struct page *p;
135
136 mem = kzalloc(sizeof(*mem), GFP_KERNEL);
137 if (!mem)
138 return -ENOMEM;
139
Minchan Kim23ba9902021-01-21 12:09:34 -0800140 p = cma_alloc(cma, count, 0, GFP_KERNEL);
Sasha Levin26b02a12015-04-14 15:44:59 -0700141 if (!p) {
142 kfree(mem);
143 return -ENOMEM;
144 }
145
146 mem->p = p;
147 mem->n = count;
148
149 cma_add_to_cma_mem_list(cma, mem);
150
151 return 0;
152}
153
154static int cma_alloc_write(void *data, u64 val)
155{
156 int pages = val;
157 struct cma *cma = data;
158
159 return cma_alloc_mem(cma, pages);
160}
zhong jianga9ea2422019-11-30 17:57:25 -0800161DEFINE_DEBUGFS_ATTRIBUTE(cma_alloc_fops, NULL, cma_alloc_write, "%llu\n");
Sasha Levin26b02a12015-04-14 15:44:59 -0700162
Yue Hu5a7f1b22019-03-05 15:49:27 -0800163static void cma_debugfs_add_one(struct cma *cma, struct dentry *root_dentry)
Sasha Levin28b24c12015-04-14 15:44:57 -0700164{
165 struct dentry *tmp;
166 char name[16];
Sasha Levin28b24c12015-04-14 15:44:57 -0700167
Prakash Guptada094e42017-08-18 15:16:21 -0700168 scnprintf(name, sizeof(name), "cma-%s", cma->name);
Sasha Levin28b24c12015-04-14 15:44:57 -0700169
Yue Hu5a7f1b22019-03-05 15:49:27 -0800170 tmp = debugfs_create_dir(name, root_dentry);
Sasha Levin28b24c12015-04-14 15:44:57 -0700171
Joe Perches0825a6f2018-06-14 15:27:58 -0700172 debugfs_create_file("alloc", 0200, tmp, cma, &cma_alloc_fops);
173 debugfs_create_file("free", 0200, tmp, cma, &cma_free_fops);
174 debugfs_create_file("base_pfn", 0444, tmp,
175 &cma->base_pfn, &cma_debugfs_fops);
176 debugfs_create_file("count", 0444, tmp, &cma->count, &cma_debugfs_fops);
177 debugfs_create_file("order_per_bit", 0444, tmp,
178 &cma->order_per_bit, &cma_debugfs_fops);
179 debugfs_create_file("used", 0444, tmp, cma, &cma_used_fops);
180 debugfs_create_file("maxchunk", 0444, tmp, cma, &cma_maxchunk_fops);
Sasha Levin28b24c12015-04-14 15:44:57 -0700181
Jakub Kicinskia2b992c2020-07-09 17:42:44 -0700182 cma->dfs_bitmap.array = (u32 *)cma->bitmap;
183 cma->dfs_bitmap.n_elements = DIV_ROUND_UP(cma_bitmap_maxno(cma),
184 BITS_PER_BYTE * sizeof(u32));
185 debugfs_create_u32_array("bitmap", 0444, tmp, &cma->dfs_bitmap);
Sasha Levin28b24c12015-04-14 15:44:57 -0700186}
187
188static int __init cma_debugfs_init(void)
189{
Yue Hu5a7f1b22019-03-05 15:49:27 -0800190 struct dentry *cma_debugfs_root;
Sasha Levin28b24c12015-04-14 15:44:57 -0700191 int i;
192
193 cma_debugfs_root = debugfs_create_dir("cma", NULL);
Sasha Levin28b24c12015-04-14 15:44:57 -0700194
195 for (i = 0; i < cma_area_count; i++)
Yue Hu5a7f1b22019-03-05 15:49:27 -0800196 cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root);
Sasha Levin28b24c12015-04-14 15:44:57 -0700197
198 return 0;
199}
200late_initcall(cma_debugfs_init);