xref: /openbmc/linux/mm/shrinker_debug.c (revision 7507f099)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/idr.h>
3 #include <linux/slab.h>
4 #include <linux/debugfs.h>
5 #include <linux/seq_file.h>
6 #include <linux/shrinker.h>
7 #include <linux/memcontrol.h>
8 
9 /* defined in vmscan.c */
10 extern struct rw_semaphore shrinker_rwsem;
11 extern struct list_head shrinker_list;
12 
13 static DEFINE_IDA(shrinker_debugfs_ida);
14 static struct dentry *shrinker_debugfs_root;
15 
16 static unsigned long shrinker_count_objects(struct shrinker *shrinker,
17 					    struct mem_cgroup *memcg,
18 					    unsigned long *count_per_node)
19 {
20 	unsigned long nr, total = 0;
21 	int nid;
22 
23 	for_each_node(nid) {
24 		if (nid == 0 || (shrinker->flags & SHRINKER_NUMA_AWARE)) {
25 			struct shrink_control sc = {
26 				.gfp_mask = GFP_KERNEL,
27 				.nid = nid,
28 				.memcg = memcg,
29 			};
30 
31 			nr = shrinker->count_objects(shrinker, &sc);
32 			if (nr == SHRINK_EMPTY)
33 				nr = 0;
34 		} else {
35 			nr = 0;
36 		}
37 
38 		count_per_node[nid] = nr;
39 		total += nr;
40 	}
41 
42 	return total;
43 }
44 
45 static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
46 {
47 	struct shrinker *shrinker = m->private;
48 	unsigned long *count_per_node;
49 	struct mem_cgroup *memcg;
50 	unsigned long total;
51 	bool memcg_aware;
52 	int ret, nid;
53 
54 	count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long), GFP_KERNEL);
55 	if (!count_per_node)
56 		return -ENOMEM;
57 
58 	ret = down_read_killable(&shrinker_rwsem);
59 	if (ret) {
60 		kfree(count_per_node);
61 		return ret;
62 	}
63 	rcu_read_lock();
64 
65 	memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE;
66 
67 	memcg = mem_cgroup_iter(NULL, NULL, NULL);
68 	do {
69 		if (memcg && !mem_cgroup_online(memcg))
70 			continue;
71 
72 		total = shrinker_count_objects(shrinker,
73 					       memcg_aware ? memcg : NULL,
74 					       count_per_node);
75 		if (total) {
76 			seq_printf(m, "%lu", mem_cgroup_ino(memcg));
77 			for_each_node(nid)
78 				seq_printf(m, " %lu", count_per_node[nid]);
79 			seq_putc(m, '\n');
80 		}
81 
82 		if (!memcg_aware) {
83 			mem_cgroup_iter_break(NULL, memcg);
84 			break;
85 		}
86 
87 		if (signal_pending(current)) {
88 			mem_cgroup_iter_break(NULL, memcg);
89 			ret = -EINTR;
90 			break;
91 		}
92 	} while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
93 
94 	rcu_read_unlock();
95 	up_read(&shrinker_rwsem);
96 
97 	kfree(count_per_node);
98 	return ret;
99 }
100 DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count);
101 
102 int shrinker_debugfs_add(struct shrinker *shrinker)
103 {
104 	struct dentry *entry;
105 	char buf[128];
106 	int id;
107 
108 	lockdep_assert_held(&shrinker_rwsem);
109 
110 	/* debugfs isn't initialized yet, add debugfs entries later. */
111 	if (!shrinker_debugfs_root)
112 		return 0;
113 
114 	id = ida_alloc(&shrinker_debugfs_ida, GFP_KERNEL);
115 	if (id < 0)
116 		return id;
117 	shrinker->debugfs_id = id;
118 
119 	snprintf(buf, sizeof(buf), "%s-%d", shrinker->name, id);
120 
121 	/* create debugfs entry */
122 	entry = debugfs_create_dir(buf, shrinker_debugfs_root);
123 	if (IS_ERR(entry)) {
124 		ida_free(&shrinker_debugfs_ida, id);
125 		return PTR_ERR(entry);
126 	}
127 	shrinker->debugfs_entry = entry;
128 
129 	debugfs_create_file("count", 0220, entry, shrinker,
130 			    &shrinker_debugfs_count_fops);
131 	return 0;
132 }
133 
134 int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...)
135 {
136 	struct dentry *entry;
137 	char buf[128];
138 	const char *new, *old;
139 	va_list ap;
140 	int ret = 0;
141 
142 	va_start(ap, fmt);
143 	new = kvasprintf_const(GFP_KERNEL, fmt, ap);
144 	va_end(ap);
145 
146 	if (!new)
147 		return -ENOMEM;
148 
149 	down_write(&shrinker_rwsem);
150 
151 	old = shrinker->name;
152 	shrinker->name = new;
153 
154 	if (shrinker->debugfs_entry) {
155 		snprintf(buf, sizeof(buf), "%s-%d", shrinker->name,
156 			 shrinker->debugfs_id);
157 
158 		entry = debugfs_rename(shrinker_debugfs_root,
159 				       shrinker->debugfs_entry,
160 				       shrinker_debugfs_root, buf);
161 		if (IS_ERR(entry))
162 			ret = PTR_ERR(entry);
163 		else
164 			shrinker->debugfs_entry = entry;
165 	}
166 
167 	up_write(&shrinker_rwsem);
168 
169 	kfree_const(old);
170 
171 	return ret;
172 }
173 EXPORT_SYMBOL(shrinker_debugfs_rename);
174 
175 void shrinker_debugfs_remove(struct shrinker *shrinker)
176 {
177 	lockdep_assert_held(&shrinker_rwsem);
178 
179 	kfree_const(shrinker->name);
180 
181 	if (!shrinker->debugfs_entry)
182 		return;
183 
184 	debugfs_remove_recursive(shrinker->debugfs_entry);
185 	ida_free(&shrinker_debugfs_ida, shrinker->debugfs_id);
186 }
187 
188 static int __init shrinker_debugfs_init(void)
189 {
190 	struct shrinker *shrinker;
191 	struct dentry *dentry;
192 	int ret = 0;
193 
194 	dentry = debugfs_create_dir("shrinker", NULL);
195 	if (IS_ERR(dentry))
196 		return PTR_ERR(dentry);
197 	shrinker_debugfs_root = dentry;
198 
199 	/* Create debugfs entries for shrinkers registered at boot */
200 	down_write(&shrinker_rwsem);
201 	list_for_each_entry(shrinker, &shrinker_list, list)
202 		if (!shrinker->debugfs_entry) {
203 			ret = shrinker_debugfs_add(shrinker);
204 			if (ret)
205 				break;
206 		}
207 	up_write(&shrinker_rwsem);
208 
209 	return ret;
210 }
211 late_initcall(shrinker_debugfs_init);
212