xref: /linux-6.15/lib/alloc_tag.c (revision 3e09c500)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/alloc_tag.h>
3 #include <linux/fs.h>
4 #include <linux/gfp.h>
5 #include <linux/module.h>
6 #include <linux/page_ext.h>
7 #include <linux/proc_fs.h>
8 #include <linux/seq_buf.h>
9 #include <linux/seq_file.h>
10 
11 #define ALLOCINFO_FILE_NAME		"allocinfo"
12 
13 #ifdef CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT
14 static bool mem_profiling_support __meminitdata = true;
15 #else
16 static bool mem_profiling_support __meminitdata;
17 #endif
18 
19 static struct codetag_type *alloc_tag_cttype;
20 
21 DEFINE_PER_CPU(struct alloc_tag_counters, _shared_alloc_tag);
22 EXPORT_SYMBOL(_shared_alloc_tag);
23 
24 DEFINE_STATIC_KEY_MAYBE(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT,
25 			mem_alloc_profiling_key);
26 
27 struct allocinfo_private {
28 	struct codetag_iterator iter;
29 	bool print_header;
30 };
31 
32 static void *allocinfo_start(struct seq_file *m, loff_t *pos)
33 {
34 	struct allocinfo_private *priv;
35 	struct codetag *ct;
36 	loff_t node = *pos;
37 
38 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
39 	m->private = priv;
40 	if (!priv)
41 		return NULL;
42 
43 	priv->print_header = (node == 0);
44 	codetag_lock_module_list(alloc_tag_cttype, true);
45 	priv->iter = codetag_get_ct_iter(alloc_tag_cttype);
46 	while ((ct = codetag_next_ct(&priv->iter)) != NULL && node)
47 		node--;
48 
49 	return ct ? priv : NULL;
50 }
51 
52 static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos)
53 {
54 	struct allocinfo_private *priv = (struct allocinfo_private *)arg;
55 	struct codetag *ct = codetag_next_ct(&priv->iter);
56 
57 	(*pos)++;
58 	if (!ct)
59 		return NULL;
60 
61 	return priv;
62 }
63 
64 static void allocinfo_stop(struct seq_file *m, void *arg)
65 {
66 	struct allocinfo_private *priv = (struct allocinfo_private *)m->private;
67 
68 	if (priv) {
69 		codetag_lock_module_list(alloc_tag_cttype, false);
70 		kfree(priv);
71 	}
72 }
73 
74 static void print_allocinfo_header(struct seq_buf *buf)
75 {
76 	/* Output format version, so we can change it. */
77 	seq_buf_printf(buf, "allocinfo - version: 1.0\n");
78 	seq_buf_printf(buf, "#     <size>  <calls> <tag info>\n");
79 }
80 
81 static void alloc_tag_to_text(struct seq_buf *out, struct codetag *ct)
82 {
83 	struct alloc_tag *tag = ct_to_alloc_tag(ct);
84 	struct alloc_tag_counters counter = alloc_tag_read(tag);
85 	s64 bytes = counter.bytes;
86 
87 	seq_buf_printf(out, "%12lli %8llu ", bytes, counter.calls);
88 	codetag_to_text(out, ct);
89 	seq_buf_putc(out, ' ');
90 	seq_buf_putc(out, '\n');
91 }
92 
93 static int allocinfo_show(struct seq_file *m, void *arg)
94 {
95 	struct allocinfo_private *priv = (struct allocinfo_private *)arg;
96 	char *bufp;
97 	size_t n = seq_get_buf(m, &bufp);
98 	struct seq_buf buf;
99 
100 	seq_buf_init(&buf, bufp, n);
101 	if (priv->print_header) {
102 		print_allocinfo_header(&buf);
103 		priv->print_header = false;
104 	}
105 	alloc_tag_to_text(&buf, priv->iter.ct);
106 	seq_commit(m, seq_buf_used(&buf));
107 	return 0;
108 }
109 
110 static const struct seq_operations allocinfo_seq_op = {
111 	.start	= allocinfo_start,
112 	.next	= allocinfo_next,
113 	.stop	= allocinfo_stop,
114 	.show	= allocinfo_show,
115 };
116 
117 size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
118 {
119 	struct codetag_iterator iter;
120 	struct codetag *ct;
121 	struct codetag_bytes n;
122 	unsigned int i, nr = 0;
123 
124 	if (can_sleep)
125 		codetag_lock_module_list(alloc_tag_cttype, true);
126 	else if (!codetag_trylock_module_list(alloc_tag_cttype))
127 		return 0;
128 
129 	iter = codetag_get_ct_iter(alloc_tag_cttype);
130 	while ((ct = codetag_next_ct(&iter))) {
131 		struct alloc_tag_counters counter = alloc_tag_read(ct_to_alloc_tag(ct));
132 
133 		n.ct	= ct;
134 		n.bytes = counter.bytes;
135 
136 		for (i = 0; i < nr; i++)
137 			if (n.bytes > tags[i].bytes)
138 				break;
139 
140 		if (i < count) {
141 			nr -= nr == count;
142 			memmove(&tags[i + 1],
143 				&tags[i],
144 				sizeof(tags[0]) * (nr - i));
145 			nr++;
146 			tags[i] = n;
147 		}
148 	}
149 
150 	codetag_lock_module_list(alloc_tag_cttype, false);
151 
152 	return nr;
153 }
154 
155 static void __init shutdown_mem_profiling(void)
156 {
157 	if (mem_alloc_profiling_enabled())
158 		static_branch_disable(&mem_alloc_profiling_key);
159 
160 	if (!mem_profiling_support)
161 		return;
162 
163 	mem_profiling_support = false;
164 }
165 
166 static void __init procfs_init(void)
167 {
168 	if (!mem_profiling_support)
169 		return;
170 
171 	if (!proc_create_seq(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_seq_op)) {
172 		pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
173 		shutdown_mem_profiling();
174 	}
175 }
176 
177 static bool alloc_tag_module_unload(struct codetag_type *cttype,
178 				    struct codetag_module *cmod)
179 {
180 	struct codetag_iterator iter = codetag_get_ct_iter(cttype);
181 	struct alloc_tag_counters counter;
182 	bool module_unused = true;
183 	struct alloc_tag *tag;
184 	struct codetag *ct;
185 
186 	for (ct = codetag_next_ct(&iter); ct; ct = codetag_next_ct(&iter)) {
187 		if (iter.cmod != cmod)
188 			continue;
189 
190 		tag = ct_to_alloc_tag(ct);
191 		counter = alloc_tag_read(tag);
192 
193 		if (WARN(counter.bytes,
194 			 "%s:%u module %s func:%s has %llu allocated at module unload",
195 			 ct->filename, ct->lineno, ct->modname, ct->function, counter.bytes))
196 			module_unused = false;
197 	}
198 
199 	return module_unused;
200 }
201 
202 static int __init setup_early_mem_profiling(char *str)
203 {
204 	bool enable;
205 
206 	if (!str || !str[0])
207 		return -EINVAL;
208 
209 	if (!strncmp(str, "never", 5)) {
210 		enable = false;
211 		mem_profiling_support = false;
212 	} else {
213 		int res;
214 
215 		res = kstrtobool(str, &enable);
216 		if (res)
217 			return res;
218 
219 		mem_profiling_support = true;
220 	}
221 
222 	if (enable != static_key_enabled(&mem_alloc_profiling_key)) {
223 		if (enable)
224 			static_branch_enable(&mem_alloc_profiling_key);
225 		else
226 			static_branch_disable(&mem_alloc_profiling_key);
227 	}
228 
229 	return 0;
230 }
231 early_param("sysctl.vm.mem_profiling", setup_early_mem_profiling);
232 
233 static __init bool need_page_alloc_tagging(void)
234 {
235 	return mem_profiling_support;
236 }
237 
238 static __init void init_page_alloc_tagging(void)
239 {
240 }
241 
242 struct page_ext_operations page_alloc_tagging_ops = {
243 	.size = sizeof(union codetag_ref),
244 	.need = need_page_alloc_tagging,
245 	.init = init_page_alloc_tagging,
246 };
247 EXPORT_SYMBOL(page_alloc_tagging_ops);
248 
249 #ifdef CONFIG_SYSCTL
250 static struct ctl_table memory_allocation_profiling_sysctls[] = {
251 	{
252 		.procname	= "mem_profiling",
253 		.data		= &mem_alloc_profiling_key,
254 #ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG
255 		.mode		= 0444,
256 #else
257 		.mode		= 0644,
258 #endif
259 		.proc_handler	= proc_do_static_key,
260 	},
261 };
262 
263 static void __init sysctl_init(void)
264 {
265 	if (!mem_profiling_support)
266 		memory_allocation_profiling_sysctls[0].mode = 0444;
267 
268 	register_sysctl_init("vm", memory_allocation_profiling_sysctls);
269 }
270 #else /* CONFIG_SYSCTL */
271 static inline void sysctl_init(void) {}
272 #endif /* CONFIG_SYSCTL */
273 
274 static int __init alloc_tag_init(void)
275 {
276 	const struct codetag_type_desc desc = {
277 		.section	= "alloc_tags",
278 		.tag_size	= sizeof(struct alloc_tag),
279 		.module_unload	= alloc_tag_module_unload,
280 	};
281 
282 	alloc_tag_cttype = codetag_register_type(&desc);
283 	if (IS_ERR(alloc_tag_cttype))
284 		return PTR_ERR(alloc_tag_cttype);
285 
286 	sysctl_init();
287 	procfs_init();
288 
289 	return 0;
290 }
291 module_init(alloc_tag_init);
292