xref: /linux/mm/alloc_tag.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
13c77682dSLorenzo Stoakes // SPDX-License-Identifier: GPL-2.0-only
23c77682dSLorenzo Stoakes #include <linux/alloc_tag.h>
33c77682dSLorenzo Stoakes #include <linux/execmem.h>
43c77682dSLorenzo Stoakes #include <linux/fs.h>
53c77682dSLorenzo Stoakes #include <linux/gfp.h>
63c77682dSLorenzo Stoakes #include <linux/kallsyms.h>
73c77682dSLorenzo Stoakes #include <linux/module.h>
81d581ab2SSuren Baghdasaryan #include <linux/mutex.h>
91d581ab2SSuren Baghdasaryan #include <linux/compat.h>
103c77682dSLorenzo Stoakes #include <linux/page_ext.h>
113c77682dSLorenzo Stoakes #include <linux/pgalloc_tag.h>
123c77682dSLorenzo Stoakes #include <linux/proc_fs.h>
133c77682dSLorenzo Stoakes #include <linux/rcupdate.h>
143c77682dSLorenzo Stoakes #include <linux/seq_buf.h>
153c77682dSLorenzo Stoakes #include <linux/seq_file.h>
163c77682dSLorenzo Stoakes #include <linux/string_choices.h>
173c77682dSLorenzo Stoakes #include <linux/vmalloc.h>
183c77682dSLorenzo Stoakes #include <linux/kmemleak.h>
191d581ab2SSuren Baghdasaryan #include <uapi/linux/alloc_tag.h>
203c77682dSLorenzo Stoakes 
216372aac4SBrendan Jackman #include "internal.h"
226372aac4SBrendan Jackman #include "page_alloc.h"
236372aac4SBrendan Jackman 
243c77682dSLorenzo Stoakes #define ALLOCINFO_FILE_NAME		"allocinfo"
253c77682dSLorenzo Stoakes #define MODULE_ALLOC_TAG_VMAP_SIZE	(100000UL * sizeof(struct alloc_tag))
263c77682dSLorenzo Stoakes #define SECTION_START(NAME)		(CODETAG_SECTION_START_PREFIX NAME)
273c77682dSLorenzo Stoakes #define SECTION_STOP(NAME)		(CODETAG_SECTION_STOP_PREFIX NAME)
283c77682dSLorenzo Stoakes 
293c77682dSLorenzo Stoakes #ifdef CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT
303c77682dSLorenzo Stoakes static bool mem_profiling_support = true;
313c77682dSLorenzo Stoakes #else
323c77682dSLorenzo Stoakes static bool mem_profiling_support;
333c77682dSLorenzo Stoakes #endif
343c77682dSLorenzo Stoakes 
353c77682dSLorenzo Stoakes /*
363c77682dSLorenzo Stoakes  * Memory allocation profiling is permanently disabled and cannot be enabled.
373c77682dSLorenzo Stoakes  * Must be called after setup_early_mem_profiling().
383c77682dSLorenzo Stoakes  */
393c77682dSLorenzo Stoakes bool mem_alloc_profiling_permanently_disabled(void)
403c77682dSLorenzo Stoakes {
413c77682dSLorenzo Stoakes 	return !mem_profiling_support;
423c77682dSLorenzo Stoakes }
433c77682dSLorenzo Stoakes 
443c77682dSLorenzo Stoakes static struct codetag_type *alloc_tag_cttype;
453c77682dSLorenzo Stoakes 
463c77682dSLorenzo Stoakes #ifdef CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU
473c77682dSLorenzo Stoakes DEFINE_PER_CPU(struct alloc_tag_counters, _shared_alloc_tag);
483c77682dSLorenzo Stoakes EXPORT_SYMBOL(_shared_alloc_tag);
493c77682dSLorenzo Stoakes #endif
503c77682dSLorenzo Stoakes 
513c77682dSLorenzo Stoakes DEFINE_STATIC_KEY_MAYBE(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT,
523c77682dSLorenzo Stoakes 			mem_alloc_profiling_key);
533c77682dSLorenzo Stoakes EXPORT_SYMBOL(mem_alloc_profiling_key);
543c77682dSLorenzo Stoakes 
553c77682dSLorenzo Stoakes DEFINE_STATIC_KEY_FALSE(mem_profiling_compressed);
563c77682dSLorenzo Stoakes 
573c77682dSLorenzo Stoakes struct alloc_tag_kernel_section kernel_tags = { NULL, 0 };
583c77682dSLorenzo Stoakes unsigned long alloc_tag_ref_mask;
593c77682dSLorenzo Stoakes int alloc_tag_ref_offs;
603c77682dSLorenzo Stoakes 
613c77682dSLorenzo Stoakes struct allocinfo_private {
623c77682dSLorenzo Stoakes 	struct codetag_iterator iter;
633c77682dSLorenzo Stoakes 	struct codetag_iterator reported_iter;
643c77682dSLorenzo Stoakes 	bool print_header;
655732a4e4SAbhishek Bapat 	struct allocinfo_filter filter;
661d581ab2SSuren Baghdasaryan 	/* ioctl uses a separate iterator not to interfere with reads */
671d581ab2SSuren Baghdasaryan 	struct codetag_iterator ioctl_iter;
681d581ab2SSuren Baghdasaryan 	bool positioned; /* seq_open_private() sets to 0 */
691d581ab2SSuren Baghdasaryan 	struct mutex ioctl_lock;
703c77682dSLorenzo Stoakes };
713c77682dSLorenzo Stoakes 
723c77682dSLorenzo Stoakes static void *allocinfo_start(struct seq_file *m, loff_t *pos)
733c77682dSLorenzo Stoakes {
743c77682dSLorenzo Stoakes 	struct allocinfo_private *priv;
753c77682dSLorenzo Stoakes 	loff_t node = *pos;
763c77682dSLorenzo Stoakes 
773c77682dSLorenzo Stoakes 	priv = (struct allocinfo_private *)m->private;
783c77682dSLorenzo Stoakes 	codetag_lock_module_list(alloc_tag_cttype);
793c77682dSLorenzo Stoakes 	if (node == 0) {
803c77682dSLorenzo Stoakes 		priv->print_header = true;
813c77682dSLorenzo Stoakes 		priv->iter = codetag_get_ct_iter(alloc_tag_cttype);
823c77682dSLorenzo Stoakes 	} else {
833c77682dSLorenzo Stoakes 		priv->iter = priv->reported_iter;
843c77682dSLorenzo Stoakes 	}
853c77682dSLorenzo Stoakes 	codetag_next_ct(&priv->iter);
863c77682dSLorenzo Stoakes 	return priv->iter.ct ? priv : NULL;
873c77682dSLorenzo Stoakes }
883c77682dSLorenzo Stoakes 
893c77682dSLorenzo Stoakes static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos)
903c77682dSLorenzo Stoakes {
913c77682dSLorenzo Stoakes 	struct allocinfo_private *priv = (struct allocinfo_private *)arg;
923c77682dSLorenzo Stoakes 	struct codetag *ct;
933c77682dSLorenzo Stoakes 
943c77682dSLorenzo Stoakes 	priv->reported_iter = priv->iter;
953c77682dSLorenzo Stoakes 	ct = codetag_next_ct(&priv->iter);
963c77682dSLorenzo Stoakes 	(*pos)++;
973c77682dSLorenzo Stoakes 	if (!ct)
983c77682dSLorenzo Stoakes 		return NULL;
993c77682dSLorenzo Stoakes 
1003c77682dSLorenzo Stoakes 	return priv;
1013c77682dSLorenzo Stoakes }
1023c77682dSLorenzo Stoakes 
1033c77682dSLorenzo Stoakes static void allocinfo_stop(struct seq_file *m, void *arg)
1043c77682dSLorenzo Stoakes {
1053c77682dSLorenzo Stoakes 	codetag_unlock_module_list(alloc_tag_cttype);
1063c77682dSLorenzo Stoakes }
1073c77682dSLorenzo Stoakes 
1083c77682dSLorenzo Stoakes static void print_allocinfo_header(struct seq_buf *buf)
1093c77682dSLorenzo Stoakes {
1103c77682dSLorenzo Stoakes 	/* Output format version, so we can change it. */
1113c77682dSLorenzo Stoakes 	seq_buf_printf(buf, "allocinfo - version: 2.0\n");
1123c77682dSLorenzo Stoakes 	seq_buf_printf(buf, "#     <size>  <calls> <tag info>\n");
1133c77682dSLorenzo Stoakes }
1143c77682dSLorenzo Stoakes 
1153c77682dSLorenzo Stoakes static void alloc_tag_to_text(struct seq_buf *out, struct codetag *ct)
1163c77682dSLorenzo Stoakes {
1173c77682dSLorenzo Stoakes 	struct alloc_tag *tag = ct_to_alloc_tag(ct);
1183c77682dSLorenzo Stoakes 	struct alloc_tag_counters counter = alloc_tag_read(tag);
1193c77682dSLorenzo Stoakes 	s64 bytes = counter.bytes;
1203c77682dSLorenzo Stoakes 
1213c77682dSLorenzo Stoakes 	seq_buf_printf(out, "%12lli %8llu ", bytes, counter.calls);
1223c77682dSLorenzo Stoakes 	codetag_to_text(out, ct);
1233c77682dSLorenzo Stoakes 	if (unlikely(alloc_tag_is_inaccurate(tag)))
1243c77682dSLorenzo Stoakes 		seq_buf_printf(out, " accurate:no");
1253c77682dSLorenzo Stoakes 	seq_buf_putc(out, ' ');
1263c77682dSLorenzo Stoakes 	seq_buf_putc(out, '\n');
1273c77682dSLorenzo Stoakes }
1283c77682dSLorenzo Stoakes 
1293c77682dSLorenzo Stoakes static int allocinfo_show(struct seq_file *m, void *arg)
1303c77682dSLorenzo Stoakes {
1313c77682dSLorenzo Stoakes 	struct allocinfo_private *priv = (struct allocinfo_private *)arg;
1323c77682dSLorenzo Stoakes 	char *bufp;
1333c77682dSLorenzo Stoakes 	size_t n = seq_get_buf(m, &bufp);
1343c77682dSLorenzo Stoakes 	struct seq_buf buf;
1353c77682dSLorenzo Stoakes 
1363c77682dSLorenzo Stoakes 	seq_buf_init(&buf, bufp, n);
1373c77682dSLorenzo Stoakes 	if (priv->print_header) {
1383c77682dSLorenzo Stoakes 		print_allocinfo_header(&buf);
1393c77682dSLorenzo Stoakes 		priv->print_header = false;
1403c77682dSLorenzo Stoakes 	}
1413c77682dSLorenzo Stoakes 	alloc_tag_to_text(&buf, priv->iter.ct);
1423c77682dSLorenzo Stoakes 	seq_commit(m, seq_buf_used(&buf));
1433c77682dSLorenzo Stoakes 	return 0;
1443c77682dSLorenzo Stoakes }
1453c77682dSLorenzo Stoakes 
1463c77682dSLorenzo Stoakes static const struct seq_operations allocinfo_seq_op = {
1473c77682dSLorenzo Stoakes 	.start	= allocinfo_start,
1483c77682dSLorenzo Stoakes 	.next	= allocinfo_next,
1493c77682dSLorenzo Stoakes 	.stop	= allocinfo_stop,
1503c77682dSLorenzo Stoakes 	.show	= allocinfo_show,
1513c77682dSLorenzo Stoakes };
1523c77682dSLorenzo Stoakes 
1531d581ab2SSuren Baghdasaryan /*
1541d581ab2SSuren Baghdasaryan  * Initializes seq_file operations and allocates private state when opening
1551d581ab2SSuren Baghdasaryan  * the /proc/allocinfo procfs entry.
1561d581ab2SSuren Baghdasaryan  */
1571d581ab2SSuren Baghdasaryan static int allocinfo_open(struct inode *inode, struct file *file)
1581d581ab2SSuren Baghdasaryan {
1591d581ab2SSuren Baghdasaryan 	int ret;
1601d581ab2SSuren Baghdasaryan 
1611d581ab2SSuren Baghdasaryan 	ret = seq_open_private(file, &allocinfo_seq_op,
1621d581ab2SSuren Baghdasaryan 			       sizeof(struct allocinfo_private));
1631d581ab2SSuren Baghdasaryan 	if (!ret) {
1641d581ab2SSuren Baghdasaryan 		struct seq_file *m = file->private_data;
1651d581ab2SSuren Baghdasaryan 		struct allocinfo_private *priv = m->private;
1661d581ab2SSuren Baghdasaryan 
1671d581ab2SSuren Baghdasaryan 		mutex_init(&priv->ioctl_lock);
1681d581ab2SSuren Baghdasaryan 	}
1691d581ab2SSuren Baghdasaryan 	return ret;
1701d581ab2SSuren Baghdasaryan }
1711d581ab2SSuren Baghdasaryan 
1721d581ab2SSuren Baghdasaryan /*
1731d581ab2SSuren Baghdasaryan  * Cleans up the seq_file state and frees up the private state allocated in
1741d581ab2SSuren Baghdasaryan  * allocinfo_open() when closing the /proc/allocinfo file descriptor.
1751d581ab2SSuren Baghdasaryan  */
1761d581ab2SSuren Baghdasaryan static int allocinfo_release(struct inode *inode, struct file *file)
1771d581ab2SSuren Baghdasaryan {
1781d581ab2SSuren Baghdasaryan 	struct seq_file *m = file->private_data;
1791d581ab2SSuren Baghdasaryan 	struct allocinfo_private *priv = m->private;
1801d581ab2SSuren Baghdasaryan 
1811d581ab2SSuren Baghdasaryan 	mutex_destroy(&priv->ioctl_lock);
1821d581ab2SSuren Baghdasaryan 	return seq_release_private(inode, file);
1831d581ab2SSuren Baghdasaryan }
1841d581ab2SSuren Baghdasaryan 
1851d581ab2SSuren Baghdasaryan /*
1861d581ab2SSuren Baghdasaryan  * Returns a pointer to the suffix of a string so that its length fits within
1871d581ab2SSuren Baghdasaryan  * ALLOCINFO_STR_SIZE, preserving the trailing characters.
1881d581ab2SSuren Baghdasaryan  * Function, file and module names often have the same prefixes, therefore
1891d581ab2SSuren Baghdasaryan  * when filtering by these criteria, we compare the last 64 characters to
1901d581ab2SSuren Baghdasaryan  * minimize the chances of name collisions
1911d581ab2SSuren Baghdasaryan  */
1921d581ab2SSuren Baghdasaryan static const char *allocinfo_str(const char *str)
1931d581ab2SSuren Baghdasaryan {
1941d581ab2SSuren Baghdasaryan 	size_t len = strlen(str);
1951d581ab2SSuren Baghdasaryan 
1961d581ab2SSuren Baghdasaryan 	/* Keep an extra space for the trailing NULL. */
1971d581ab2SSuren Baghdasaryan 	if (len >= ALLOCINFO_STR_SIZE)
1981d581ab2SSuren Baghdasaryan 		str += (len - ALLOCINFO_STR_SIZE) + 1;
1991d581ab2SSuren Baghdasaryan 	return str;
2001d581ab2SSuren Baghdasaryan }
2011d581ab2SSuren Baghdasaryan 
2021d581ab2SSuren Baghdasaryan /* Copy a string and trim from the beginning if it's too long */
2031d581ab2SSuren Baghdasaryan static void allocinfo_copy_str(char *dest, const char *src)
2041d581ab2SSuren Baghdasaryan {
2051d581ab2SSuren Baghdasaryan 	strscpy_pad(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
2061d581ab2SSuren Baghdasaryan }
2071d581ab2SSuren Baghdasaryan 
2085732a4e4SAbhishek Bapat /* Compare two strings and only consider the trimmed suffix if s1 is too long */
2095732a4e4SAbhishek Bapat static int allocinfo_cmp_str(const char *str, const char *template)
2105732a4e4SAbhishek Bapat {
2115732a4e4SAbhishek Bapat 	return strncmp(allocinfo_str(str), template, ALLOCINFO_STR_SIZE);
2125732a4e4SAbhishek Bapat }
2135732a4e4SAbhishek Bapat 
2146f6769eaSAbhishek Bapat /* Fetch the per-CPU counters */
2156f6769eaSAbhishek Bapat static inline struct alloc_tag_counters allocinfo_prefetch_counters(struct codetag *ct)
2166f6769eaSAbhishek Bapat {
2176f6769eaSAbhishek Bapat 	return alloc_tag_read(ct_to_alloc_tag(ct));
2186f6769eaSAbhishek Bapat }
2196f6769eaSAbhishek Bapat 
2201d581ab2SSuren Baghdasaryan /*
2211d581ab2SSuren Baghdasaryan  * Populates the UAPI allocinfo_tag_data structure with active runtime
2221d581ab2SSuren Baghdasaryan  * profiling counters extracted from the given kernel codetag.
2231d581ab2SSuren Baghdasaryan  */
2241d581ab2SSuren Baghdasaryan static void allocinfo_to_params(struct codetag *ct,
2256f6769eaSAbhishek Bapat 				struct allocinfo_tag_data *data,
2266f6769eaSAbhishek Bapat 				struct alloc_tag_counters *counters)
2271d581ab2SSuren Baghdasaryan {
2281d581ab2SSuren Baghdasaryan 	if (ct->modname)
2291d581ab2SSuren Baghdasaryan 		allocinfo_copy_str(data->tag.modname, ct->modname);
2301d581ab2SSuren Baghdasaryan 	else
2311d581ab2SSuren Baghdasaryan 		data->tag.modname[0] = '\0';
2321d581ab2SSuren Baghdasaryan 	allocinfo_copy_str(data->tag.function, ct->function);
2331d581ab2SSuren Baghdasaryan 	allocinfo_copy_str(data->tag.filename, ct->filename);
2341d581ab2SSuren Baghdasaryan 	data->tag.lineno = ct->lineno;
2356f6769eaSAbhishek Bapat 	data->counter.bytes = counters->bytes;
2366f6769eaSAbhishek Bapat 	data->counter.calls = counters->calls;
2376f6769eaSAbhishek Bapat 	data->counter.accurate = !alloc_tag_is_inaccurate(ct_to_alloc_tag(ct));
2381d581ab2SSuren Baghdasaryan }
2391d581ab2SSuren Baghdasaryan 
2401d581ab2SSuren Baghdasaryan /*
2411d581ab2SSuren Baghdasaryan  * Retrieves the unique content ID representing the current allocation tag module
2421d581ab2SSuren Baghdasaryan  * layout, allowing userspace to detect if modules were loaded / unloaded.
2431d581ab2SSuren Baghdasaryan  */
2441d581ab2SSuren Baghdasaryan static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
2451d581ab2SSuren Baghdasaryan {
2461d581ab2SSuren Baghdasaryan 	struct allocinfo_content_id params;
2471d581ab2SSuren Baghdasaryan 
2481d581ab2SSuren Baghdasaryan 	codetag_lock_module_list(alloc_tag_cttype);
2491d581ab2SSuren Baghdasaryan 	params.id = codetag_get_content_id(alloc_tag_cttype);
2501d581ab2SSuren Baghdasaryan 	codetag_unlock_module_list(alloc_tag_cttype);
2511d581ab2SSuren Baghdasaryan 	if (copy_to_user(arg, &params, sizeof(params)))
2521d581ab2SSuren Baghdasaryan 		return -EFAULT;
2531d581ab2SSuren Baghdasaryan 
2541d581ab2SSuren Baghdasaryan 	return 0;
2551d581ab2SSuren Baghdasaryan }
2561d581ab2SSuren Baghdasaryan 
2571d581ab2SSuren Baghdasaryan /*
2585732a4e4SAbhishek Bapat  * Verifies whether a given codetag satisfies the active filtering criteria by
2595732a4e4SAbhishek Bapat  * matching its characteristics against the specified filter.
2605732a4e4SAbhishek Bapat  */
2616f6769eaSAbhishek Bapat static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter,
2626f6769eaSAbhishek Bapat 			   struct alloc_tag_counters *counters,
2636f6769eaSAbhishek Bapat 			   bool *fetched_counters)
2645732a4e4SAbhishek Bapat {
265*33588e0bSAbhishek Bapat 	bool inaccurate;
266*33588e0bSAbhishek Bapat 
2675732a4e4SAbhishek Bapat 	if (!filter || !filter->mask)
2685732a4e4SAbhishek Bapat 		return true;
2695732a4e4SAbhishek Bapat 
2705732a4e4SAbhishek Bapat 	if (filter->mask & ALLOCINFO_FILTER_MASK_MODNAME) {
2715732a4e4SAbhishek Bapat 		/* user wants to filter by modname but ct->modname is NULL */
2725732a4e4SAbhishek Bapat 		if (!ct->modname) {
2735732a4e4SAbhishek Bapat 			/* validate if user was attempting to filter for built-in allocations */
2745732a4e4SAbhishek Bapat 			if (filter->fields.modname[0] != '\0')
2755732a4e4SAbhishek Bapat 				return false;
2765732a4e4SAbhishek Bapat 		} else if (allocinfo_cmp_str(ct->modname, filter->fields.modname))
2775732a4e4SAbhishek Bapat 			return false;
2785732a4e4SAbhishek Bapat 	}
2795732a4e4SAbhishek Bapat 
2805732a4e4SAbhishek Bapat 	if ((filter->mask & ALLOCINFO_FILTER_MASK_FUNCTION) &&
2815732a4e4SAbhishek Bapat 	    ct->function && allocinfo_cmp_str(ct->function, filter->fields.function))
2825732a4e4SAbhishek Bapat 		return false;
2835732a4e4SAbhishek Bapat 
2845732a4e4SAbhishek Bapat 	if ((filter->mask & ALLOCINFO_FILTER_MASK_FILENAME) &&
2855732a4e4SAbhishek Bapat 	    ct->filename && allocinfo_cmp_str(ct->filename, filter->fields.filename))
2865732a4e4SAbhishek Bapat 		return false;
2875732a4e4SAbhishek Bapat 
2885732a4e4SAbhishek Bapat 	if ((filter->mask & ALLOCINFO_FILTER_MASK_LINENO) &&
2895732a4e4SAbhishek Bapat 	    ct->lineno != filter->fields.lineno)
2905732a4e4SAbhishek Bapat 		return false;
2915732a4e4SAbhishek Bapat 
292*33588e0bSAbhishek Bapat 	if (filter->mask & ALLOCINFO_FILTER_MASK_INACCURATE) {
293*33588e0bSAbhishek Bapat 		inaccurate = !!(ct->flags & CODETAG_FLAG_INACCURATE);
294*33588e0bSAbhishek Bapat 		if (inaccurate != !!(filter->inaccurate))
295*33588e0bSAbhishek Bapat 			return false;
296*33588e0bSAbhishek Bapat 	}
297*33588e0bSAbhishek Bapat 
2986f6769eaSAbhishek Bapat 	if (filter->mask & (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE)) {
2996f6769eaSAbhishek Bapat 		if (!*fetched_counters) {
3006f6769eaSAbhishek Bapat 			*counters = allocinfo_prefetch_counters(ct);
3016f6769eaSAbhishek Bapat 			*fetched_counters = true;
3026f6769eaSAbhishek Bapat 		}
3036f6769eaSAbhishek Bapat 		if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
3046f6769eaSAbhishek Bapat 		    counters->bytes < filter->min_size)
3056f6769eaSAbhishek Bapat 			return false;
3066f6769eaSAbhishek Bapat 		if ((filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
3076f6769eaSAbhishek Bapat 		    counters->bytes > filter->max_size)
3086f6769eaSAbhishek Bapat 			return false;
3096f6769eaSAbhishek Bapat 	}
3106f6769eaSAbhishek Bapat 
3115732a4e4SAbhishek Bapat 	return true;
3125732a4e4SAbhishek Bapat }
3135732a4e4SAbhishek Bapat 
3145732a4e4SAbhishek Bapat /*
3151d581ab2SSuren Baghdasaryan  * Seeks the ioctl iterator to the specified 0-indexed tag position, reads its
3161d581ab2SSuren Baghdasaryan  * profiling data and returns it to userspace.
3171d581ab2SSuren Baghdasaryan  */
3181d581ab2SSuren Baghdasaryan static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
3191d581ab2SSuren Baghdasaryan {
3201d581ab2SSuren Baghdasaryan 	struct allocinfo_private *priv;
3211d581ab2SSuren Baghdasaryan 	struct codetag *ct;
3221d581ab2SSuren Baghdasaryan 	struct allocinfo_get_at params = {0};
3235732a4e4SAbhishek Bapat 	__u64 skip_count;
3246f6769eaSAbhishek Bapat 	struct alloc_tag_counters counters;
3256f6769eaSAbhishek Bapat 	bool fetched_counters;
3261d581ab2SSuren Baghdasaryan 
3271d581ab2SSuren Baghdasaryan 	if (copy_from_user(&params, arg, sizeof(params)))
3281d581ab2SSuren Baghdasaryan 		return -EFAULT;
3291d581ab2SSuren Baghdasaryan 
3305732a4e4SAbhishek Bapat 	if (params.filter.mask & ~ALLOCINFO_FILTER_MASKS)
3315732a4e4SAbhishek Bapat 		return -EINVAL;
3325732a4e4SAbhishek Bapat 
3336f6769eaSAbhishek Bapat 	if ((params.filter.mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
3346f6769eaSAbhishek Bapat 	    (params.filter.mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
3356f6769eaSAbhishek Bapat 	    params.filter.min_size > params.filter.max_size)
3366f6769eaSAbhishek Bapat 		return -EINVAL;
3376f6769eaSAbhishek Bapat 
3381d581ab2SSuren Baghdasaryan 	priv = m->private;
3391d581ab2SSuren Baghdasaryan 
3401d581ab2SSuren Baghdasaryan 	mutex_lock(&priv->ioctl_lock);
3411d581ab2SSuren Baghdasaryan 	codetag_lock_module_list(alloc_tag_cttype);
3421d581ab2SSuren Baghdasaryan 
3435732a4e4SAbhishek Bapat 	if (params.pos >= codetag_get_count(alloc_tag_cttype)) {
3441d581ab2SSuren Baghdasaryan 		codetag_unlock_module_list(alloc_tag_cttype);
3451d581ab2SSuren Baghdasaryan 		mutex_unlock(&priv->ioctl_lock);
3461d581ab2SSuren Baghdasaryan 		return -ENOENT;
3471d581ab2SSuren Baghdasaryan 	}
3481d581ab2SSuren Baghdasaryan 
3495732a4e4SAbhishek Bapat 	skip_count = params.pos;
3505732a4e4SAbhishek Bapat 
3515732a4e4SAbhishek Bapat 	if (params.filter.mask)
3525732a4e4SAbhishek Bapat 		priv->filter = params.filter;
3535732a4e4SAbhishek Bapat 	else
3545732a4e4SAbhishek Bapat 		priv->filter.mask = 0;
3555732a4e4SAbhishek Bapat 
3561d581ab2SSuren Baghdasaryan 	/* Find the codetag */
3571d581ab2SSuren Baghdasaryan 	priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
3581d581ab2SSuren Baghdasaryan 	ct = codetag_next_ct(&priv->ioctl_iter);
3595732a4e4SAbhishek Bapat 
3605732a4e4SAbhishek Bapat 	while (ct) {
3616f6769eaSAbhishek Bapat 		fetched_counters = false;
3626f6769eaSAbhishek Bapat 		if (matches_filter(ct, &priv->filter, &counters, &fetched_counters)) {
3635732a4e4SAbhishek Bapat 			if (skip_count == 0)
3645732a4e4SAbhishek Bapat 				break;
3655732a4e4SAbhishek Bapat 			skip_count--;
3665732a4e4SAbhishek Bapat 		}
3671d581ab2SSuren Baghdasaryan 		ct = codetag_next_ct(&priv->ioctl_iter);
3685732a4e4SAbhishek Bapat 	}
3695732a4e4SAbhishek Bapat 
3701d581ab2SSuren Baghdasaryan 	if (ct) {
3716f6769eaSAbhishek Bapat 		if (!fetched_counters)
3726f6769eaSAbhishek Bapat 			counters = allocinfo_prefetch_counters(ct);
3736f6769eaSAbhishek Bapat 		allocinfo_to_params(ct, &params.data, &counters);
3741d581ab2SSuren Baghdasaryan 		priv->positioned = true;
3751d581ab2SSuren Baghdasaryan 	}
3761d581ab2SSuren Baghdasaryan 
3771d581ab2SSuren Baghdasaryan 	codetag_unlock_module_list(alloc_tag_cttype);
3781d581ab2SSuren Baghdasaryan 	mutex_unlock(&priv->ioctl_lock);
3791d581ab2SSuren Baghdasaryan 
3801d581ab2SSuren Baghdasaryan 	if (!ct)
3811d581ab2SSuren Baghdasaryan 		return -ENOENT;
3821d581ab2SSuren Baghdasaryan 
3831d581ab2SSuren Baghdasaryan 	if (copy_to_user(arg, &params, sizeof(params)))
3841d581ab2SSuren Baghdasaryan 		return -EFAULT;
3851d581ab2SSuren Baghdasaryan 
3861d581ab2SSuren Baghdasaryan 	return 0;
3871d581ab2SSuren Baghdasaryan }
3881d581ab2SSuren Baghdasaryan 
3891d581ab2SSuren Baghdasaryan /*
3901d581ab2SSuren Baghdasaryan  * Advances the ioctl iterator to the next allocation tag in the sequence and
3911d581ab2SSuren Baghdasaryan  * returns its profiling data to userspace.
3921d581ab2SSuren Baghdasaryan  */
3931d581ab2SSuren Baghdasaryan static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
3941d581ab2SSuren Baghdasaryan {
3951d581ab2SSuren Baghdasaryan 	struct allocinfo_private *priv;
3961d581ab2SSuren Baghdasaryan 	struct codetag *ct;
3971d581ab2SSuren Baghdasaryan 	struct allocinfo_tag_data params;
3981d581ab2SSuren Baghdasaryan 	int ret = 0;
3996f6769eaSAbhishek Bapat 	struct alloc_tag_counters counters;
4006f6769eaSAbhishek Bapat 	bool fetched_counters;
4011d581ab2SSuren Baghdasaryan 
4021d581ab2SSuren Baghdasaryan 	memset(&params, 0, sizeof(params));
4031d581ab2SSuren Baghdasaryan 	priv = m->private;
4041d581ab2SSuren Baghdasaryan 
4051d581ab2SSuren Baghdasaryan 	mutex_lock(&priv->ioctl_lock);
4061d581ab2SSuren Baghdasaryan 	codetag_lock_module_list(alloc_tag_cttype);
4071d581ab2SSuren Baghdasaryan 
4081d581ab2SSuren Baghdasaryan 	if (!priv->positioned) {
4091d581ab2SSuren Baghdasaryan 		priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
4101d581ab2SSuren Baghdasaryan 		priv->positioned = true;
4111d581ab2SSuren Baghdasaryan 	}
4121d581ab2SSuren Baghdasaryan 
4131d581ab2SSuren Baghdasaryan 	ct = codetag_next_ct(&priv->ioctl_iter);
4146f6769eaSAbhishek Bapat 	while (ct) {
4156f6769eaSAbhishek Bapat 		fetched_counters = false;
4166f6769eaSAbhishek Bapat 		if (matches_filter(ct, &priv->filter, &counters, &fetched_counters))
4176f6769eaSAbhishek Bapat 			break;
4185732a4e4SAbhishek Bapat 		ct = codetag_next_ct(&priv->ioctl_iter);
4196f6769eaSAbhishek Bapat 	}
4201d581ab2SSuren Baghdasaryan 
4216f6769eaSAbhishek Bapat 	if (ct) {
4226f6769eaSAbhishek Bapat 		if (!fetched_counters)
4236f6769eaSAbhishek Bapat 			counters = allocinfo_prefetch_counters(ct);
4246f6769eaSAbhishek Bapat 		allocinfo_to_params(ct, &params, &counters);
4256f6769eaSAbhishek Bapat 	}
4261d581ab2SSuren Baghdasaryan 	if (!ct) {
4271d581ab2SSuren Baghdasaryan 		priv->positioned = false;
4281d581ab2SSuren Baghdasaryan 		ret = -ENOENT;
4291d581ab2SSuren Baghdasaryan 	}
4301d581ab2SSuren Baghdasaryan 	codetag_unlock_module_list(alloc_tag_cttype);
4311d581ab2SSuren Baghdasaryan 	mutex_unlock(&priv->ioctl_lock);
4321d581ab2SSuren Baghdasaryan 
4331d581ab2SSuren Baghdasaryan 	if (ret == 0) {
4341d581ab2SSuren Baghdasaryan 		if (copy_to_user(arg, &params, sizeof(params)))
4351d581ab2SSuren Baghdasaryan 			return -EFAULT;
4361d581ab2SSuren Baghdasaryan 	}
4371d581ab2SSuren Baghdasaryan 	return ret;
4381d581ab2SSuren Baghdasaryan }
4391d581ab2SSuren Baghdasaryan 
4401d581ab2SSuren Baghdasaryan /*
4411d581ab2SSuren Baghdasaryan  * Entry point ioctl function for /proc/allocinfo routing requests to fetch the
4421d581ab2SSuren Baghdasaryan  * layout content ID, seek to a specific tag, or read sequential tags.
4431d581ab2SSuren Baghdasaryan  */
4441d581ab2SSuren Baghdasaryan static long allocinfo_ioctl(struct file *file, unsigned int cmd,
4451d581ab2SSuren Baghdasaryan 			    unsigned long __arg)
4461d581ab2SSuren Baghdasaryan {
4471d581ab2SSuren Baghdasaryan 	void __user *arg = (void __user *)__arg;
4481d581ab2SSuren Baghdasaryan 	int ret;
4491d581ab2SSuren Baghdasaryan 
4501d581ab2SSuren Baghdasaryan 	switch (cmd) {
4511d581ab2SSuren Baghdasaryan 	case ALLOCINFO_IOC_CONTENT_ID:
4521d581ab2SSuren Baghdasaryan 		ret = allocinfo_ioctl_get_content_id(file->private_data, arg);
4531d581ab2SSuren Baghdasaryan 		break;
4541d581ab2SSuren Baghdasaryan 	case ALLOCINFO_IOC_GET_AT:
4551d581ab2SSuren Baghdasaryan 		ret = allocinfo_ioctl_get_at(file->private_data, arg);
4561d581ab2SSuren Baghdasaryan 		break;
4571d581ab2SSuren Baghdasaryan 	case ALLOCINFO_IOC_GET_NEXT:
4581d581ab2SSuren Baghdasaryan 		ret = allocinfo_ioctl_get_next(file->private_data, arg);
4591d581ab2SSuren Baghdasaryan 		break;
4601d581ab2SSuren Baghdasaryan 	default:
4611d581ab2SSuren Baghdasaryan 		ret = -ENOIOCTLCMD;
4621d581ab2SSuren Baghdasaryan 		break;
4631d581ab2SSuren Baghdasaryan 	}
4641d581ab2SSuren Baghdasaryan 
4651d581ab2SSuren Baghdasaryan 	return ret;
4661d581ab2SSuren Baghdasaryan }
4671d581ab2SSuren Baghdasaryan 
4681d581ab2SSuren Baghdasaryan #ifdef CONFIG_COMPAT
4691d581ab2SSuren Baghdasaryan static long allocinfo_compat_ioctl(struct file *file, unsigned int cmd,
4701d581ab2SSuren Baghdasaryan 				   unsigned long arg)
4711d581ab2SSuren Baghdasaryan {
4721d581ab2SSuren Baghdasaryan 	return allocinfo_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
4731d581ab2SSuren Baghdasaryan }
4741d581ab2SSuren Baghdasaryan #endif
4751d581ab2SSuren Baghdasaryan 
4761d581ab2SSuren Baghdasaryan static const struct proc_ops allocinfo_proc_ops = {
4771d581ab2SSuren Baghdasaryan 	.proc_open		= allocinfo_open,
4781d581ab2SSuren Baghdasaryan 	.proc_read_iter		= seq_read_iter,
4791d581ab2SSuren Baghdasaryan 	.proc_lseek		= seq_lseek,
4801d581ab2SSuren Baghdasaryan 	.proc_release		= allocinfo_release,
4811d581ab2SSuren Baghdasaryan 	.proc_ioctl		= allocinfo_ioctl,
4821d581ab2SSuren Baghdasaryan #ifdef CONFIG_COMPAT
4831d581ab2SSuren Baghdasaryan 	.proc_compat_ioctl	= allocinfo_compat_ioctl,
4841d581ab2SSuren Baghdasaryan #endif
4851d581ab2SSuren Baghdasaryan };
4861d581ab2SSuren Baghdasaryan 
4873c77682dSLorenzo Stoakes size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
4883c77682dSLorenzo Stoakes {
4893c77682dSLorenzo Stoakes 	struct codetag_iterator iter;
4903c77682dSLorenzo Stoakes 	struct codetag *ct;
4913c77682dSLorenzo Stoakes 	struct codetag_bytes n;
4923c77682dSLorenzo Stoakes 	unsigned int i, nr = 0;
4933c77682dSLorenzo Stoakes 
4943c77682dSLorenzo Stoakes 	if (IS_ERR_OR_NULL(alloc_tag_cttype))
4953c77682dSLorenzo Stoakes 		return 0;
4963c77682dSLorenzo Stoakes 
4973c77682dSLorenzo Stoakes 	if (can_sleep)
4983c77682dSLorenzo Stoakes 		codetag_lock_module_list(alloc_tag_cttype);
4993c77682dSLorenzo Stoakes 	else if (!codetag_trylock_module_list(alloc_tag_cttype))
5003c77682dSLorenzo Stoakes 		return 0;
5013c77682dSLorenzo Stoakes 
5023c77682dSLorenzo Stoakes 	iter = codetag_get_ct_iter(alloc_tag_cttype);
5033c77682dSLorenzo Stoakes 	while ((ct = codetag_next_ct(&iter))) {
5043c77682dSLorenzo Stoakes 		struct alloc_tag_counters counter = alloc_tag_read(ct_to_alloc_tag(ct));
5053c77682dSLorenzo Stoakes 
5063c77682dSLorenzo Stoakes 		n.ct	= ct;
5073c77682dSLorenzo Stoakes 		n.bytes = counter.bytes;
5083c77682dSLorenzo Stoakes 
5093c77682dSLorenzo Stoakes 		for (i = 0; i < nr; i++)
5103c77682dSLorenzo Stoakes 			if (n.bytes > tags[i].bytes)
5113c77682dSLorenzo Stoakes 				break;
5123c77682dSLorenzo Stoakes 
5133c77682dSLorenzo Stoakes 		if (i < count) {
5143c77682dSLorenzo Stoakes 			nr -= nr == count;
5153c77682dSLorenzo Stoakes 			memmove(&tags[i + 1],
5163c77682dSLorenzo Stoakes 				&tags[i],
5173c77682dSLorenzo Stoakes 				sizeof(tags[0]) * (nr - i));
5183c77682dSLorenzo Stoakes 			nr++;
5193c77682dSLorenzo Stoakes 			tags[i] = n;
5203c77682dSLorenzo Stoakes 		}
5213c77682dSLorenzo Stoakes 	}
5223c77682dSLorenzo Stoakes 
5233c77682dSLorenzo Stoakes 	codetag_unlock_module_list(alloc_tag_cttype);
5243c77682dSLorenzo Stoakes 
5253c77682dSLorenzo Stoakes 	return nr;
5263c77682dSLorenzo Stoakes }
5273c77682dSLorenzo Stoakes 
5283c77682dSLorenzo Stoakes void pgalloc_tag_split(struct folio *folio, int old_order, int new_order)
5293c77682dSLorenzo Stoakes {
5303c77682dSLorenzo Stoakes 	int i;
5313c77682dSLorenzo Stoakes 	struct alloc_tag *tag;
5323c77682dSLorenzo Stoakes 	unsigned int nr_pages = 1 << new_order;
5333c77682dSLorenzo Stoakes 
5343c77682dSLorenzo Stoakes 	if (!mem_alloc_profiling_enabled())
5353c77682dSLorenzo Stoakes 		return;
5363c77682dSLorenzo Stoakes 
5373c77682dSLorenzo Stoakes 	tag = __pgalloc_tag_get(&folio->page);
5383c77682dSLorenzo Stoakes 	if (!tag)
5393c77682dSLorenzo Stoakes 		return;
5403c77682dSLorenzo Stoakes 
5413c77682dSLorenzo Stoakes 	for (i = nr_pages; i < (1 << old_order); i += nr_pages) {
5423c77682dSLorenzo Stoakes 		union pgtag_ref_handle handle;
5433c77682dSLorenzo Stoakes 		union codetag_ref ref;
5443c77682dSLorenzo Stoakes 
5453c77682dSLorenzo Stoakes 		if (get_page_tag_ref(folio_page(folio, i), &ref, &handle)) {
5463c77682dSLorenzo Stoakes 			/* Set new reference to point to the original tag */
5473c77682dSLorenzo Stoakes 			alloc_tag_ref_set(&ref, tag);
5483c77682dSLorenzo Stoakes 			update_page_tag_ref(handle, &ref);
5493c77682dSLorenzo Stoakes 			put_page_tag_ref(handle);
5503c77682dSLorenzo Stoakes 		}
5513c77682dSLorenzo Stoakes 	}
5523c77682dSLorenzo Stoakes }
5533c77682dSLorenzo Stoakes 
5543c77682dSLorenzo Stoakes void pgalloc_tag_swap(struct folio *new, struct folio *old)
5553c77682dSLorenzo Stoakes {
5563c77682dSLorenzo Stoakes 	union pgtag_ref_handle handle_old, handle_new;
5573c77682dSLorenzo Stoakes 	union codetag_ref ref_old, ref_new;
5583c77682dSLorenzo Stoakes 	struct alloc_tag *tag_old, *tag_new;
5593c77682dSLorenzo Stoakes 
5603c77682dSLorenzo Stoakes 	if (!mem_alloc_profiling_enabled())
5613c77682dSLorenzo Stoakes 		return;
5623c77682dSLorenzo Stoakes 
5633c77682dSLorenzo Stoakes 	tag_old = __pgalloc_tag_get(&old->page);
5643c77682dSLorenzo Stoakes 	if (!tag_old)
5653c77682dSLorenzo Stoakes 		return;
5663c77682dSLorenzo Stoakes 	tag_new = __pgalloc_tag_get(&new->page);
5673c77682dSLorenzo Stoakes 	if (!tag_new)
5683c77682dSLorenzo Stoakes 		return;
5693c77682dSLorenzo Stoakes 
5703c77682dSLorenzo Stoakes 	if (!get_page_tag_ref(&old->page, &ref_old, &handle_old))
5713c77682dSLorenzo Stoakes 		return;
5723c77682dSLorenzo Stoakes 	if (!get_page_tag_ref(&new->page, &ref_new, &handle_new)) {
5733c77682dSLorenzo Stoakes 		put_page_tag_ref(handle_old);
5743c77682dSLorenzo Stoakes 		return;
5753c77682dSLorenzo Stoakes 	}
5763c77682dSLorenzo Stoakes 
5773c77682dSLorenzo Stoakes 	/*
5783c77682dSLorenzo Stoakes 	 * Clear tag references to avoid debug warning when using
5793c77682dSLorenzo Stoakes 	 * __alloc_tag_ref_set() with non-empty reference.
5803c77682dSLorenzo Stoakes 	 */
5813c77682dSLorenzo Stoakes 	set_codetag_empty(&ref_old);
5823c77682dSLorenzo Stoakes 	set_codetag_empty(&ref_new);
5833c77682dSLorenzo Stoakes 
5843c77682dSLorenzo Stoakes 	/* swap tags */
5853c77682dSLorenzo Stoakes 	__alloc_tag_ref_set(&ref_old, tag_new);
5863c77682dSLorenzo Stoakes 	update_page_tag_ref(handle_old, &ref_old);
5873c77682dSLorenzo Stoakes 	__alloc_tag_ref_set(&ref_new, tag_old);
5883c77682dSLorenzo Stoakes 	update_page_tag_ref(handle_new, &ref_new);
5893c77682dSLorenzo Stoakes 
5903c77682dSLorenzo Stoakes 	put_page_tag_ref(handle_old);
5913c77682dSLorenzo Stoakes 	put_page_tag_ref(handle_new);
5923c77682dSLorenzo Stoakes }
5933c77682dSLorenzo Stoakes 
5943c77682dSLorenzo Stoakes static void shutdown_mem_profiling(bool remove_file)
5953c77682dSLorenzo Stoakes {
5963c77682dSLorenzo Stoakes 	if (mem_alloc_profiling_enabled())
5973c77682dSLorenzo Stoakes 		static_branch_disable(&mem_alloc_profiling_key);
5983c77682dSLorenzo Stoakes 
5993c77682dSLorenzo Stoakes 	if (!mem_profiling_support)
6003c77682dSLorenzo Stoakes 		return;
6013c77682dSLorenzo Stoakes 
6023c77682dSLorenzo Stoakes 	if (remove_file)
6033c77682dSLorenzo Stoakes 		remove_proc_entry(ALLOCINFO_FILE_NAME, NULL);
6043c77682dSLorenzo Stoakes 	mem_profiling_support = false;
6053c77682dSLorenzo Stoakes }
6063c77682dSLorenzo Stoakes 
6073c77682dSLorenzo Stoakes void __init alloc_tag_sec_init(void)
6083c77682dSLorenzo Stoakes {
6093c77682dSLorenzo Stoakes 	struct alloc_tag *last_codetag;
6103c77682dSLorenzo Stoakes 
6113c77682dSLorenzo Stoakes 	if (!mem_profiling_support)
6123c77682dSLorenzo Stoakes 		return;
6133c77682dSLorenzo Stoakes 
6143c77682dSLorenzo Stoakes 	if (!static_key_enabled(&mem_profiling_compressed))
6153c77682dSLorenzo Stoakes 		return;
6163c77682dSLorenzo Stoakes 
6173c77682dSLorenzo Stoakes 	kernel_tags.first_tag = (struct alloc_tag *)kallsyms_lookup_name(
6183c77682dSLorenzo Stoakes 					SECTION_START(ALLOC_TAG_SECTION_NAME));
6193c77682dSLorenzo Stoakes 	last_codetag = (struct alloc_tag *)kallsyms_lookup_name(
6203c77682dSLorenzo Stoakes 					SECTION_STOP(ALLOC_TAG_SECTION_NAME));
6213c77682dSLorenzo Stoakes 	kernel_tags.count = last_codetag - kernel_tags.first_tag;
6223c77682dSLorenzo Stoakes 
6233c77682dSLorenzo Stoakes 	/* Check if kernel tags fit into page flags */
6243c77682dSLorenzo Stoakes 	if (kernel_tags.count > (1UL << NR_UNUSED_PAGEFLAG_BITS)) {
6253c77682dSLorenzo Stoakes 		shutdown_mem_profiling(false); /* allocinfo file does not exist yet */
6263c77682dSLorenzo Stoakes 		pr_err("%lu allocation tags cannot be references using %d available page flag bits. Memory allocation profiling is disabled!\n",
6273c77682dSLorenzo Stoakes 			kernel_tags.count, NR_UNUSED_PAGEFLAG_BITS);
6283c77682dSLorenzo Stoakes 		return;
6293c77682dSLorenzo Stoakes 	}
6303c77682dSLorenzo Stoakes 
6313c77682dSLorenzo Stoakes 	alloc_tag_ref_offs = (LRU_REFS_PGOFF - NR_UNUSED_PAGEFLAG_BITS);
6323c77682dSLorenzo Stoakes 	alloc_tag_ref_mask = ((1UL << NR_UNUSED_PAGEFLAG_BITS) - 1);
6333c77682dSLorenzo Stoakes 	pr_debug("Memory allocation profiling compression is using %d page flag bits!\n",
6343c77682dSLorenzo Stoakes 		 NR_UNUSED_PAGEFLAG_BITS);
6353c77682dSLorenzo Stoakes }
6363c77682dSLorenzo Stoakes 
6373c77682dSLorenzo Stoakes #ifdef CONFIG_MODULES
6383c77682dSLorenzo Stoakes 
6393c77682dSLorenzo Stoakes static struct maple_tree mod_area_mt = MTREE_INIT(mod_area_mt, MT_FLAGS_ALLOC_RANGE);
6403c77682dSLorenzo Stoakes static struct vm_struct *vm_module_tags;
6413c77682dSLorenzo Stoakes /* A dummy object used to indicate an unloaded module */
6423c77682dSLorenzo Stoakes static struct module unloaded_mod;
6433c77682dSLorenzo Stoakes /* A dummy object used to indicate a module prepended area */
6443c77682dSLorenzo Stoakes static struct module prepend_mod;
6453c77682dSLorenzo Stoakes 
6463c77682dSLorenzo Stoakes struct alloc_tag_module_section module_tags;
6473c77682dSLorenzo Stoakes 
6483c77682dSLorenzo Stoakes static inline unsigned long alloc_tag_align(unsigned long val)
6493c77682dSLorenzo Stoakes {
6503c77682dSLorenzo Stoakes 	if (!static_key_enabled(&mem_profiling_compressed)) {
6513c77682dSLorenzo Stoakes 		/* No alignment requirements when we are not indexing the tags */
6523c77682dSLorenzo Stoakes 		return val;
6533c77682dSLorenzo Stoakes 	}
6543c77682dSLorenzo Stoakes 
6553c77682dSLorenzo Stoakes 	if (val % sizeof(struct alloc_tag) == 0)
6563c77682dSLorenzo Stoakes 		return val;
6573c77682dSLorenzo Stoakes 	return ((val / sizeof(struct alloc_tag)) + 1) * sizeof(struct alloc_tag);
6583c77682dSLorenzo Stoakes }
6593c77682dSLorenzo Stoakes 
6603c77682dSLorenzo Stoakes static bool ensure_alignment(unsigned long align, unsigned int *prepend)
6613c77682dSLorenzo Stoakes {
6623c77682dSLorenzo Stoakes 	if (!static_key_enabled(&mem_profiling_compressed)) {
6633c77682dSLorenzo Stoakes 		/* No alignment requirements when we are not indexing the tags */
6643c77682dSLorenzo Stoakes 		return true;
6653c77682dSLorenzo Stoakes 	}
6663c77682dSLorenzo Stoakes 
6673c77682dSLorenzo Stoakes 	/*
6683c77682dSLorenzo Stoakes 	 * If alloc_tag size is not a multiple of required alignment, tag
6693c77682dSLorenzo Stoakes 	 * indexing does not work.
6703c77682dSLorenzo Stoakes 	 */
6713c77682dSLorenzo Stoakes 	if (!IS_ALIGNED(sizeof(struct alloc_tag), align))
6723c77682dSLorenzo Stoakes 		return false;
6733c77682dSLorenzo Stoakes 
6743c77682dSLorenzo Stoakes 	/* Ensure prepend consumes multiple of alloc_tag-sized blocks */
6753c77682dSLorenzo Stoakes 	if (*prepend)
6763c77682dSLorenzo Stoakes 		*prepend = alloc_tag_align(*prepend);
6773c77682dSLorenzo Stoakes 
6783c77682dSLorenzo Stoakes 	return true;
6793c77682dSLorenzo Stoakes }
6803c77682dSLorenzo Stoakes 
6813c77682dSLorenzo Stoakes static inline bool tags_addressable(void)
6823c77682dSLorenzo Stoakes {
6833c77682dSLorenzo Stoakes 	unsigned long tag_idx_count;
6843c77682dSLorenzo Stoakes 
6853c77682dSLorenzo Stoakes 	if (!static_key_enabled(&mem_profiling_compressed))
6863c77682dSLorenzo Stoakes 		return true; /* with page_ext tags are always addressable */
6873c77682dSLorenzo Stoakes 
6883c77682dSLorenzo Stoakes 	tag_idx_count = CODETAG_ID_FIRST + kernel_tags.count +
6893c77682dSLorenzo Stoakes 			module_tags.size / sizeof(struct alloc_tag);
6903c77682dSLorenzo Stoakes 
6913c77682dSLorenzo Stoakes 	return tag_idx_count < (1UL << NR_UNUSED_PAGEFLAG_BITS);
6923c77682dSLorenzo Stoakes }
6933c77682dSLorenzo Stoakes 
6943c77682dSLorenzo Stoakes static bool needs_section_mem(struct module *mod, unsigned long size)
6953c77682dSLorenzo Stoakes {
6963c77682dSLorenzo Stoakes 	if (!mem_profiling_support)
6973c77682dSLorenzo Stoakes 		return false;
6983c77682dSLorenzo Stoakes 
6993c77682dSLorenzo Stoakes 	return size >= sizeof(struct alloc_tag);
7003c77682dSLorenzo Stoakes }
7013c77682dSLorenzo Stoakes 
7023c77682dSLorenzo Stoakes static bool clean_unused_counters(struct alloc_tag *start_tag,
7033c77682dSLorenzo Stoakes 				  struct alloc_tag *end_tag)
7043c77682dSLorenzo Stoakes {
7053c77682dSLorenzo Stoakes 	struct alloc_tag *tag;
7063c77682dSLorenzo Stoakes 	bool ret = true;
7073c77682dSLorenzo Stoakes 
7083c77682dSLorenzo Stoakes 	for (tag = start_tag; tag <= end_tag; tag++) {
7093c77682dSLorenzo Stoakes 		struct alloc_tag_counters counter;
7103c77682dSLorenzo Stoakes 
7113c77682dSLorenzo Stoakes 		if (!tag->counters)
7123c77682dSLorenzo Stoakes 			continue;
7133c77682dSLorenzo Stoakes 
7143c77682dSLorenzo Stoakes 		counter = alloc_tag_read(tag);
7153c77682dSLorenzo Stoakes 		if (!counter.bytes) {
7163c77682dSLorenzo Stoakes 			free_percpu(tag->counters);
7173c77682dSLorenzo Stoakes 			tag->counters = NULL;
7183c77682dSLorenzo Stoakes 		} else {
7193c77682dSLorenzo Stoakes 			ret = false;
7203c77682dSLorenzo Stoakes 		}
7213c77682dSLorenzo Stoakes 	}
7223c77682dSLorenzo Stoakes 
7233c77682dSLorenzo Stoakes 	return ret;
7243c77682dSLorenzo Stoakes }
7253c77682dSLorenzo Stoakes 
7263c77682dSLorenzo Stoakes /* Called with mod_area_mt locked */
7273c77682dSLorenzo Stoakes static void clean_unused_module_areas_locked(void)
7283c77682dSLorenzo Stoakes {
7293c77682dSLorenzo Stoakes 	MA_STATE(mas, &mod_area_mt, 0, module_tags.size);
7303c77682dSLorenzo Stoakes 	struct module *val;
7313c77682dSLorenzo Stoakes 
7323c77682dSLorenzo Stoakes 	mas_for_each(&mas, val, module_tags.size) {
7333c77682dSLorenzo Stoakes 		struct alloc_tag *start_tag;
7343c77682dSLorenzo Stoakes 		struct alloc_tag *end_tag;
7353c77682dSLorenzo Stoakes 
7363c77682dSLorenzo Stoakes 		if (val != &unloaded_mod)
7373c77682dSLorenzo Stoakes 			continue;
7383c77682dSLorenzo Stoakes 
7393c77682dSLorenzo Stoakes 		/* Release area if all tags are unused */
7403c77682dSLorenzo Stoakes 		start_tag = (struct alloc_tag *)(module_tags.start_addr + mas.index);
7413c77682dSLorenzo Stoakes 		end_tag = (struct alloc_tag *)(module_tags.start_addr + mas.last);
7423c77682dSLorenzo Stoakes 		if (clean_unused_counters(start_tag, end_tag))
7433c77682dSLorenzo Stoakes 			mas_erase(&mas);
7443c77682dSLorenzo Stoakes 	}
7453c77682dSLorenzo Stoakes }
7463c77682dSLorenzo Stoakes 
7473c77682dSLorenzo Stoakes /* Called with mod_area_mt locked */
7483c77682dSLorenzo Stoakes static bool find_aligned_area(struct ma_state *mas, unsigned long section_size,
7493c77682dSLorenzo Stoakes 			      unsigned long size, unsigned int prepend, unsigned long align)
7503c77682dSLorenzo Stoakes {
7513c77682dSLorenzo Stoakes 	bool cleanup_done = false;
7523c77682dSLorenzo Stoakes 
7533c77682dSLorenzo Stoakes repeat:
7543c77682dSLorenzo Stoakes 	/* Try finding exact size and hope the start is aligned */
7553c77682dSLorenzo Stoakes 	if (!mas_empty_area(mas, 0, section_size - 1, prepend + size)) {
7563c77682dSLorenzo Stoakes 		if (IS_ALIGNED(mas->index + prepend, align))
7573c77682dSLorenzo Stoakes 			return true;
7583c77682dSLorenzo Stoakes 
7593c77682dSLorenzo Stoakes 		/* Try finding larger area to align later */
7603c77682dSLorenzo Stoakes 		mas_reset(mas);
7613c77682dSLorenzo Stoakes 		if (!mas_empty_area(mas, 0, section_size - 1,
7623c77682dSLorenzo Stoakes 				    size + prepend + align - 1))
7633c77682dSLorenzo Stoakes 			return true;
7643c77682dSLorenzo Stoakes 	}
7653c77682dSLorenzo Stoakes 
7663c77682dSLorenzo Stoakes 	/* No free area, try cleanup stale data and repeat the search once */
7673c77682dSLorenzo Stoakes 	if (!cleanup_done) {
7683c77682dSLorenzo Stoakes 		clean_unused_module_areas_locked();
7693c77682dSLorenzo Stoakes 		cleanup_done = true;
7703c77682dSLorenzo Stoakes 		mas_reset(mas);
7713c77682dSLorenzo Stoakes 		goto repeat;
7723c77682dSLorenzo Stoakes 	}
7733c77682dSLorenzo Stoakes 
7743c77682dSLorenzo Stoakes 	return false;
7753c77682dSLorenzo Stoakes }
7763c77682dSLorenzo Stoakes 
7773c77682dSLorenzo Stoakes static int vm_module_tags_populate(void)
7783c77682dSLorenzo Stoakes {
7793c77682dSLorenzo Stoakes 	unsigned long phys_end = ALIGN_DOWN(module_tags.start_addr, PAGE_SIZE) +
7803c77682dSLorenzo Stoakes 				 (vm_module_tags->nr_pages << PAGE_SHIFT);
7813c77682dSLorenzo Stoakes 	unsigned long new_end = module_tags.start_addr + module_tags.size;
7823c77682dSLorenzo Stoakes 
7833c77682dSLorenzo Stoakes 	if (phys_end < new_end) {
7843c77682dSLorenzo Stoakes 		struct page **next_page = vm_module_tags->pages + vm_module_tags->nr_pages;
7853c77682dSLorenzo Stoakes 		unsigned long old_shadow_end = ALIGN(phys_end, MODULE_ALIGN);
7863c77682dSLorenzo Stoakes 		unsigned long new_shadow_end = ALIGN(new_end, MODULE_ALIGN);
7873c77682dSLorenzo Stoakes 		unsigned long more_pages;
7883c77682dSLorenzo Stoakes 		unsigned long nr = 0;
7893c77682dSLorenzo Stoakes 
7903c77682dSLorenzo Stoakes 		more_pages = ALIGN(new_end - phys_end, PAGE_SIZE) >> PAGE_SHIFT;
7913c77682dSLorenzo Stoakes 		while (nr < more_pages) {
7923c77682dSLorenzo Stoakes 			unsigned long allocated;
7933c77682dSLorenzo Stoakes 
7943c77682dSLorenzo Stoakes 			allocated = alloc_pages_bulk_node(GFP_KERNEL | __GFP_NOWARN,
7953c77682dSLorenzo Stoakes 				NUMA_NO_NODE, more_pages - nr, next_page + nr);
7963c77682dSLorenzo Stoakes 
7973c77682dSLorenzo Stoakes 			if (!allocated)
7983c77682dSLorenzo Stoakes 				break;
7993c77682dSLorenzo Stoakes 			nr += allocated;
8003c77682dSLorenzo Stoakes 		}
8013c77682dSLorenzo Stoakes 
8023c77682dSLorenzo Stoakes 		if (nr < more_pages ||
8033c77682dSLorenzo Stoakes 		    vmap_pages_range(phys_end, phys_end + (nr << PAGE_SHIFT), PAGE_KERNEL,
8043c77682dSLorenzo Stoakes 				     next_page, PAGE_SHIFT) < 0) {
8053c77682dSLorenzo Stoakes 			release_pages_arg arg = { .pages = next_page };
8063c77682dSLorenzo Stoakes 
8073c77682dSLorenzo Stoakes 			/* Clean up and error out */
8083c77682dSLorenzo Stoakes 			release_pages(arg, nr);
8093c77682dSLorenzo Stoakes 			return -ENOMEM;
8103c77682dSLorenzo Stoakes 		}
8113c77682dSLorenzo Stoakes 
8123c77682dSLorenzo Stoakes 		vm_module_tags->nr_pages += nr;
8133c77682dSLorenzo Stoakes 
8143c77682dSLorenzo Stoakes 		/*
8153c77682dSLorenzo Stoakes 		 * Kasan allocates 1 byte of shadow for every 8 bytes of data.
8163c77682dSLorenzo Stoakes 		 * When kasan_alloc_module_shadow allocates shadow memory,
8173c77682dSLorenzo Stoakes 		 * its unit of allocation is a page.
8183c77682dSLorenzo Stoakes 		 * Therefore, here we need to align to MODULE_ALIGN.
8193c77682dSLorenzo Stoakes 		 */
8203c77682dSLorenzo Stoakes 		if (old_shadow_end < new_shadow_end)
8213c77682dSLorenzo Stoakes 			kasan_alloc_module_shadow((void *)old_shadow_end,
8223c77682dSLorenzo Stoakes 						  new_shadow_end - old_shadow_end,
8233c77682dSLorenzo Stoakes 						  GFP_KERNEL);
8243c77682dSLorenzo Stoakes 	}
8253c77682dSLorenzo Stoakes 
8263c77682dSLorenzo Stoakes 	/*
8273c77682dSLorenzo Stoakes 	 * Mark the pages as accessible, now that they are mapped.
8283c77682dSLorenzo Stoakes 	 * With hardware tag-based KASAN, marking is skipped for
8293c77682dSLorenzo Stoakes 	 * non-VM_ALLOC mappings, see __kasan_unpoison_vmalloc().
8303c77682dSLorenzo Stoakes 	 */
8313c77682dSLorenzo Stoakes 	kasan_unpoison_vmalloc((void *)module_tags.start_addr,
8323c77682dSLorenzo Stoakes 				new_end - module_tags.start_addr,
8333c77682dSLorenzo Stoakes 				KASAN_VMALLOC_PROT_NORMAL);
8343c77682dSLorenzo Stoakes 
8353c77682dSLorenzo Stoakes 	return 0;
8363c77682dSLorenzo Stoakes }
8373c77682dSLorenzo Stoakes 
8383c77682dSLorenzo Stoakes static void *reserve_module_tags(struct module *mod, unsigned long size,
8393c77682dSLorenzo Stoakes 				 unsigned int prepend, unsigned long align)
8403c77682dSLorenzo Stoakes {
8413c77682dSLorenzo Stoakes 	unsigned long section_size = module_tags.end_addr - module_tags.start_addr;
8423c77682dSLorenzo Stoakes 	MA_STATE(mas, &mod_area_mt, 0, section_size - 1);
8433c77682dSLorenzo Stoakes 	unsigned long offset;
8443c77682dSLorenzo Stoakes 	void *ret = NULL;
8453c77682dSLorenzo Stoakes 
8463c77682dSLorenzo Stoakes 	/* If no tags return error */
8473c77682dSLorenzo Stoakes 	if (size < sizeof(struct alloc_tag))
8483c77682dSLorenzo Stoakes 		return ERR_PTR(-EINVAL);
8493c77682dSLorenzo Stoakes 
8503c77682dSLorenzo Stoakes 	/*
8513c77682dSLorenzo Stoakes 	 * align is always power of 2, so we can use IS_ALIGNED and ALIGN.
8523c77682dSLorenzo Stoakes 	 * align 0 or 1 means no alignment, to simplify set to 1.
8533c77682dSLorenzo Stoakes 	 */
8543c77682dSLorenzo Stoakes 	if (!align)
8553c77682dSLorenzo Stoakes 		align = 1;
8563c77682dSLorenzo Stoakes 
8573c77682dSLorenzo Stoakes 	if (!ensure_alignment(align, &prepend)) {
8583c77682dSLorenzo Stoakes 		shutdown_mem_profiling(true);
8593c77682dSLorenzo Stoakes 		pr_err("%s: alignment %lu is incompatible with allocation tag indexing. Memory allocation profiling is disabled!\n",
8603c77682dSLorenzo Stoakes 			mod->name, align);
8613c77682dSLorenzo Stoakes 		return ERR_PTR(-EINVAL);
8623c77682dSLorenzo Stoakes 	}
8633c77682dSLorenzo Stoakes 
8643c77682dSLorenzo Stoakes 	mas_lock(&mas);
8653c77682dSLorenzo Stoakes 	if (!find_aligned_area(&mas, section_size, size, prepend, align)) {
8663c77682dSLorenzo Stoakes 		ret = ERR_PTR(-ENOMEM);
8673c77682dSLorenzo Stoakes 		goto unlock;
8683c77682dSLorenzo Stoakes 	}
8693c77682dSLorenzo Stoakes 
8703c77682dSLorenzo Stoakes 	/* Mark found area as reserved */
8713c77682dSLorenzo Stoakes 	offset = mas.index;
8723c77682dSLorenzo Stoakes 	offset += prepend;
8733c77682dSLorenzo Stoakes 	offset = ALIGN(offset, align);
8743c77682dSLorenzo Stoakes 	if (offset != mas.index) {
8753c77682dSLorenzo Stoakes 		unsigned long pad_start = mas.index;
8763c77682dSLorenzo Stoakes 
8773c77682dSLorenzo Stoakes 		mas.last = offset - 1;
8783c77682dSLorenzo Stoakes 		mas_store(&mas, &prepend_mod);
8793c77682dSLorenzo Stoakes 		if (mas_is_err(&mas)) {
8803c77682dSLorenzo Stoakes 			ret = ERR_PTR(xa_err(mas.node));
8813c77682dSLorenzo Stoakes 			goto unlock;
8823c77682dSLorenzo Stoakes 		}
8833c77682dSLorenzo Stoakes 		mas.index = offset;
8843c77682dSLorenzo Stoakes 		mas.last = offset + size - 1;
8853c77682dSLorenzo Stoakes 		mas_store(&mas, mod);
8863c77682dSLorenzo Stoakes 		if (mas_is_err(&mas)) {
8873c77682dSLorenzo Stoakes 			mas.index = pad_start;
8883c77682dSLorenzo Stoakes 			mas_erase(&mas);
8893c77682dSLorenzo Stoakes 			ret = ERR_PTR(xa_err(mas.node));
8903c77682dSLorenzo Stoakes 		}
8913c77682dSLorenzo Stoakes 	} else {
8923c77682dSLorenzo Stoakes 		mas.last = offset + size - 1;
8933c77682dSLorenzo Stoakes 		mas_store(&mas, mod);
8943c77682dSLorenzo Stoakes 		if (mas_is_err(&mas))
8953c77682dSLorenzo Stoakes 			ret = ERR_PTR(xa_err(mas.node));
8963c77682dSLorenzo Stoakes 	}
8973c77682dSLorenzo Stoakes unlock:
8983c77682dSLorenzo Stoakes 	mas_unlock(&mas);
8993c77682dSLorenzo Stoakes 
9003c77682dSLorenzo Stoakes 	if (IS_ERR(ret))
9013c77682dSLorenzo Stoakes 		return ret;
9023c77682dSLorenzo Stoakes 
9033c77682dSLorenzo Stoakes 	if (module_tags.size < offset + size) {
9043c77682dSLorenzo Stoakes 		int grow_res;
9053c77682dSLorenzo Stoakes 
9063c77682dSLorenzo Stoakes 		module_tags.size = offset + size;
9073c77682dSLorenzo Stoakes 		if (mem_alloc_profiling_enabled() && !tags_addressable()) {
9083c77682dSLorenzo Stoakes 			shutdown_mem_profiling(true);
9093c77682dSLorenzo Stoakes 			pr_warn("With module %s there are too many tags to fit in %d page flag bits. Memory allocation profiling is disabled!\n",
9103c77682dSLorenzo Stoakes 				mod->name, NR_UNUSED_PAGEFLAG_BITS);
9113c77682dSLorenzo Stoakes 		}
9123c77682dSLorenzo Stoakes 
9133c77682dSLorenzo Stoakes 		grow_res = vm_module_tags_populate();
9143c77682dSLorenzo Stoakes 		if (grow_res) {
9153c77682dSLorenzo Stoakes 			shutdown_mem_profiling(true);
9163c77682dSLorenzo Stoakes 			pr_err("Failed to allocate memory for allocation tags in the module %s. Memory allocation profiling is disabled!\n",
9173c77682dSLorenzo Stoakes 			       mod->name);
9183c77682dSLorenzo Stoakes 			return ERR_PTR(grow_res);
9193c77682dSLorenzo Stoakes 		}
9203c77682dSLorenzo Stoakes 	}
9213c77682dSLorenzo Stoakes 
9223c77682dSLorenzo Stoakes 	return (struct alloc_tag *)(module_tags.start_addr + offset);
9233c77682dSLorenzo Stoakes }
9243c77682dSLorenzo Stoakes 
9253c77682dSLorenzo Stoakes static void release_module_tags(struct module *mod, bool used)
9263c77682dSLorenzo Stoakes {
9273c77682dSLorenzo Stoakes 	MA_STATE(mas, &mod_area_mt, module_tags.size, module_tags.size);
9283c77682dSLorenzo Stoakes 	struct alloc_tag *start_tag;
9293c77682dSLorenzo Stoakes 	struct alloc_tag *end_tag;
9303c77682dSLorenzo Stoakes 	struct module *val;
9313c77682dSLorenzo Stoakes 
9323c77682dSLorenzo Stoakes 	mas_lock(&mas);
9333c77682dSLorenzo Stoakes 	mas_for_each_rev(&mas, val, 0)
9343c77682dSLorenzo Stoakes 		if (val == mod)
9353c77682dSLorenzo Stoakes 			break;
9363c77682dSLorenzo Stoakes 
9373c77682dSLorenzo Stoakes 	if (!val) /* module not found */
9383c77682dSLorenzo Stoakes 		goto out;
9393c77682dSLorenzo Stoakes 
9403c77682dSLorenzo Stoakes 	if (!used)
9413c77682dSLorenzo Stoakes 		goto release_area;
9423c77682dSLorenzo Stoakes 
9433c77682dSLorenzo Stoakes 	start_tag = (struct alloc_tag *)(module_tags.start_addr + mas.index);
9443c77682dSLorenzo Stoakes 	end_tag = (struct alloc_tag *)(module_tags.start_addr + mas.last);
9453c77682dSLorenzo Stoakes 	if (!clean_unused_counters(start_tag, end_tag)) {
9463c77682dSLorenzo Stoakes 		struct alloc_tag *tag;
9473c77682dSLorenzo Stoakes 
9483c77682dSLorenzo Stoakes 		for (tag = start_tag; tag <= end_tag; tag++) {
9493c77682dSLorenzo Stoakes 			struct alloc_tag_counters counter;
9503c77682dSLorenzo Stoakes 
9513c77682dSLorenzo Stoakes 			if (!tag->counters)
9523c77682dSLorenzo Stoakes 				continue;
9533c77682dSLorenzo Stoakes 
9543c77682dSLorenzo Stoakes 			counter = alloc_tag_read(tag);
9553c77682dSLorenzo Stoakes 			pr_info("%s:%u module %s func:%s has %llu allocated at module unload\n",
9563c77682dSLorenzo Stoakes 				tag->ct.filename, tag->ct.lineno, tag->ct.modname,
9573c77682dSLorenzo Stoakes 				tag->ct.function, counter.bytes);
9583c77682dSLorenzo Stoakes 		}
9593c77682dSLorenzo Stoakes 	} else {
9603c77682dSLorenzo Stoakes 		used = false;
9613c77682dSLorenzo Stoakes 	}
9623c77682dSLorenzo Stoakes release_area:
9633c77682dSLorenzo Stoakes 	mas_store(&mas, used ? &unloaded_mod : NULL);
9643c77682dSLorenzo Stoakes 	val = mas_prev_range(&mas, 0);
9653c77682dSLorenzo Stoakes 	if (val == &prepend_mod)
9663c77682dSLorenzo Stoakes 		mas_store(&mas, NULL);
9673c77682dSLorenzo Stoakes out:
9683c77682dSLorenzo Stoakes 	mas_unlock(&mas);
9693c77682dSLorenzo Stoakes }
9703c77682dSLorenzo Stoakes 
9713c77682dSLorenzo Stoakes static int load_module(struct module *mod, struct codetag *start, struct codetag *stop)
9723c77682dSLorenzo Stoakes {
9733c77682dSLorenzo Stoakes 	/* Allocate module alloc_tag percpu counters */
9743c77682dSLorenzo Stoakes 	struct alloc_tag *start_tag;
9753c77682dSLorenzo Stoakes 	struct alloc_tag *stop_tag;
9763c77682dSLorenzo Stoakes 	struct alloc_tag *tag;
9773c77682dSLorenzo Stoakes 
9783c77682dSLorenzo Stoakes 	/* percpu counters for core allocations are already statically allocated */
9793c77682dSLorenzo Stoakes 	if (!mod)
9803c77682dSLorenzo Stoakes 		return 0;
9813c77682dSLorenzo Stoakes 
9823c77682dSLorenzo Stoakes 	start_tag = ct_to_alloc_tag(start);
9833c77682dSLorenzo Stoakes 	stop_tag = ct_to_alloc_tag(stop);
9843c77682dSLorenzo Stoakes 	for (tag = start_tag; tag < stop_tag; tag++) {
9853c77682dSLorenzo Stoakes 		WARN_ON(tag->counters);
9863c77682dSLorenzo Stoakes 		tag->counters = alloc_percpu(struct alloc_tag_counters);
9873c77682dSLorenzo Stoakes 		if (!tag->counters) {
9883c77682dSLorenzo Stoakes 			while (--tag >= start_tag) {
9893c77682dSLorenzo Stoakes 				free_percpu(tag->counters);
9903c77682dSLorenzo Stoakes 				tag->counters = NULL;
9913c77682dSLorenzo Stoakes 			}
9923c77682dSLorenzo Stoakes 			pr_err("Failed to allocate memory for allocation tag percpu counters in the module %s\n",
9933c77682dSLorenzo Stoakes 			       mod->name);
9943c77682dSLorenzo Stoakes 			return -ENOMEM;
9953c77682dSLorenzo Stoakes 		}
9963c77682dSLorenzo Stoakes 
9973c77682dSLorenzo Stoakes 		/*
9983c77682dSLorenzo Stoakes 		 * Avoid a kmemleak false positive. The pointer to the counters is stored
9993c77682dSLorenzo Stoakes 		 * in the alloc_tag section of the module and cannot be directly accessed.
10003c77682dSLorenzo Stoakes 		 */
10013c77682dSLorenzo Stoakes 		kmemleak_ignore_percpu(tag->counters);
10023c77682dSLorenzo Stoakes 	}
10033c77682dSLorenzo Stoakes 	return 0;
10043c77682dSLorenzo Stoakes }
10053c77682dSLorenzo Stoakes 
10063c77682dSLorenzo Stoakes static void replace_module(struct module *mod, struct module *new_mod)
10073c77682dSLorenzo Stoakes {
10083c77682dSLorenzo Stoakes 	MA_STATE(mas, &mod_area_mt, 0, module_tags.size);
10093c77682dSLorenzo Stoakes 	struct module *val;
10103c77682dSLorenzo Stoakes 
10113c77682dSLorenzo Stoakes 	mas_lock(&mas);
10123c77682dSLorenzo Stoakes 	mas_for_each(&mas, val, module_tags.size) {
10133c77682dSLorenzo Stoakes 		if (val != mod)
10143c77682dSLorenzo Stoakes 			continue;
10153c77682dSLorenzo Stoakes 
10163c77682dSLorenzo Stoakes 		mas_store_gfp(&mas, new_mod, GFP_KERNEL);
10173c77682dSLorenzo Stoakes 		break;
10183c77682dSLorenzo Stoakes 	}
10193c77682dSLorenzo Stoakes 	mas_unlock(&mas);
10203c77682dSLorenzo Stoakes }
10213c77682dSLorenzo Stoakes 
10223c77682dSLorenzo Stoakes static int __init alloc_mod_tags_mem(void)
10233c77682dSLorenzo Stoakes {
10243c77682dSLorenzo Stoakes 	/* Map space to copy allocation tags */
10253c77682dSLorenzo Stoakes 	vm_module_tags = execmem_vmap(MODULE_ALLOC_TAG_VMAP_SIZE);
10263c77682dSLorenzo Stoakes 	if (!vm_module_tags) {
10273c77682dSLorenzo Stoakes 		pr_err("Failed to map %lu bytes for module allocation tags\n",
10283c77682dSLorenzo Stoakes 			MODULE_ALLOC_TAG_VMAP_SIZE);
10293c77682dSLorenzo Stoakes 		module_tags.start_addr = 0;
10303c77682dSLorenzo Stoakes 		return -ENOMEM;
10313c77682dSLorenzo Stoakes 	}
10323c77682dSLorenzo Stoakes 
10333c77682dSLorenzo Stoakes 	vm_module_tags->pages = kmalloc_objs(struct page *,
10343c77682dSLorenzo Stoakes 					     get_vm_area_size(vm_module_tags) >> PAGE_SHIFT,
10353c77682dSLorenzo Stoakes 					     GFP_KERNEL | __GFP_ZERO);
10363c77682dSLorenzo Stoakes 	if (!vm_module_tags->pages) {
10373c77682dSLorenzo Stoakes 		free_vm_area(vm_module_tags);
10383c77682dSLorenzo Stoakes 		return -ENOMEM;
10393c77682dSLorenzo Stoakes 	}
10403c77682dSLorenzo Stoakes 
10413c77682dSLorenzo Stoakes 	module_tags.start_addr = (unsigned long)vm_module_tags->addr;
10423c77682dSLorenzo Stoakes 	module_tags.end_addr = module_tags.start_addr + MODULE_ALLOC_TAG_VMAP_SIZE;
10433c77682dSLorenzo Stoakes 	/* Ensure the base is alloc_tag aligned when required for indexing */
10443c77682dSLorenzo Stoakes 	module_tags.start_addr = alloc_tag_align(module_tags.start_addr);
10453c77682dSLorenzo Stoakes 
10463c77682dSLorenzo Stoakes 	return 0;
10473c77682dSLorenzo Stoakes }
10483c77682dSLorenzo Stoakes 
10493c77682dSLorenzo Stoakes static void __init free_mod_tags_mem(void)
10503c77682dSLorenzo Stoakes {
10513c77682dSLorenzo Stoakes 	release_pages_arg arg = { .pages = vm_module_tags->pages };
10523c77682dSLorenzo Stoakes 
10533c77682dSLorenzo Stoakes 	module_tags.start_addr = 0;
10543c77682dSLorenzo Stoakes 	release_pages(arg, vm_module_tags->nr_pages);
10553c77682dSLorenzo Stoakes 	kfree(vm_module_tags->pages);
10563c77682dSLorenzo Stoakes 	free_vm_area(vm_module_tags);
10573c77682dSLorenzo Stoakes }
10583c77682dSLorenzo Stoakes 
10593c77682dSLorenzo Stoakes #else /* CONFIG_MODULES */
10603c77682dSLorenzo Stoakes 
10613c77682dSLorenzo Stoakes static inline int alloc_mod_tags_mem(void) { return 0; }
10623c77682dSLorenzo Stoakes static inline void free_mod_tags_mem(void) {}
10633c77682dSLorenzo Stoakes 
10643c77682dSLorenzo Stoakes #endif /* CONFIG_MODULES */
10653c77682dSLorenzo Stoakes 
10663c77682dSLorenzo Stoakes /* See: Documentation/mm/allocation-profiling.rst */
10673c77682dSLorenzo Stoakes static int __init setup_early_mem_profiling(char *str)
10683c77682dSLorenzo Stoakes {
10693c77682dSLorenzo Stoakes 	bool compressed = false;
10703c77682dSLorenzo Stoakes 	bool enable;
10713c77682dSLorenzo Stoakes 
10723c77682dSLorenzo Stoakes 	if (!str || !str[0])
10733c77682dSLorenzo Stoakes 		return -EINVAL;
10743c77682dSLorenzo Stoakes 
10753c77682dSLorenzo Stoakes 	if (!strncmp(str, "never", 5)) {
10763c77682dSLorenzo Stoakes 		enable = false;
10773c77682dSLorenzo Stoakes 		mem_profiling_support = false;
10783c77682dSLorenzo Stoakes 		pr_info("Memory allocation profiling is disabled!\n");
10793c77682dSLorenzo Stoakes 	} else {
10803c77682dSLorenzo Stoakes 		char *token = strsep(&str, ",");
10813c77682dSLorenzo Stoakes 
10823c77682dSLorenzo Stoakes 		if (kstrtobool(token, &enable))
10833c77682dSLorenzo Stoakes 			return -EINVAL;
10843c77682dSLorenzo Stoakes 
10853c77682dSLorenzo Stoakes 		if (str) {
10863c77682dSLorenzo Stoakes 
10873c77682dSLorenzo Stoakes 			if (strcmp(str, "compressed"))
10883c77682dSLorenzo Stoakes 				return -EINVAL;
10893c77682dSLorenzo Stoakes 
10903c77682dSLorenzo Stoakes 			compressed = true;
10913c77682dSLorenzo Stoakes 		}
10923c77682dSLorenzo Stoakes 		mem_profiling_support = true;
10933c77682dSLorenzo Stoakes 		pr_info("Memory allocation profiling is enabled %s compression and is turned %s!\n",
10943c77682dSLorenzo Stoakes 			compressed ? "with" : "without", str_on_off(enable));
10953c77682dSLorenzo Stoakes 	}
10963c77682dSLorenzo Stoakes 
10973c77682dSLorenzo Stoakes 	if (enable != mem_alloc_profiling_enabled()) {
10983c77682dSLorenzo Stoakes 		if (enable)
10993c77682dSLorenzo Stoakes 			static_branch_enable(&mem_alloc_profiling_key);
11003c77682dSLorenzo Stoakes 		else
11013c77682dSLorenzo Stoakes 			static_branch_disable(&mem_alloc_profiling_key);
11023c77682dSLorenzo Stoakes 	}
11033c77682dSLorenzo Stoakes 	if (compressed != static_key_enabled(&mem_profiling_compressed)) {
11043c77682dSLorenzo Stoakes 		if (compressed)
11053c77682dSLorenzo Stoakes 			static_branch_enable(&mem_profiling_compressed);
11063c77682dSLorenzo Stoakes 		else
11073c77682dSLorenzo Stoakes 			static_branch_disable(&mem_profiling_compressed);
11083c77682dSLorenzo Stoakes 	}
11093c77682dSLorenzo Stoakes 
11103c77682dSLorenzo Stoakes 	return 0;
11113c77682dSLorenzo Stoakes }
11123c77682dSLorenzo Stoakes early_param("sysctl.vm.mem_profiling", setup_early_mem_profiling);
11133c77682dSLorenzo Stoakes 
11143c77682dSLorenzo Stoakes static __init bool need_page_alloc_tagging(void)
11153c77682dSLorenzo Stoakes {
11163c77682dSLorenzo Stoakes 	if (static_key_enabled(&mem_profiling_compressed))
11173c77682dSLorenzo Stoakes 		return false;
11183c77682dSLorenzo Stoakes 
11193c77682dSLorenzo Stoakes 	return mem_profiling_support;
11203c77682dSLorenzo Stoakes }
11213c77682dSLorenzo Stoakes 
11223c77682dSLorenzo Stoakes #ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG
11233c77682dSLorenzo Stoakes /*
11243c77682dSLorenzo Stoakes  * Track page allocations before page_ext is initialized.
11253c77682dSLorenzo Stoakes  * Some pages are allocated before page_ext becomes available, leaving
11263c77682dSLorenzo Stoakes  * their codetag uninitialized. Track these early PFNs so we can clear
11273c77682dSLorenzo Stoakes  * their codetag refs later to avoid warnings when they are freed.
11283c77682dSLorenzo Stoakes  *
11293c77682dSLorenzo Stoakes  * Each page is cast to a pfn_pool: the first few bytes hold metadata
11303c77682dSLorenzo Stoakes  * (next pointer and slot count), the remainder stores PFNs.
11313c77682dSLorenzo Stoakes  */
11323c77682dSLorenzo Stoakes struct pfn_pool {
11333c77682dSLorenzo Stoakes 	struct pfn_pool *next;
11343c77682dSLorenzo Stoakes 	atomic_t count;
11353c77682dSLorenzo Stoakes 	unsigned long pfns[];
11363c77682dSLorenzo Stoakes };
11373c77682dSLorenzo Stoakes 
11383c77682dSLorenzo Stoakes #define PFN_POOL_SIZE			((PAGE_SIZE - offsetof(struct pfn_pool, pfns)) / \
11393c77682dSLorenzo Stoakes 					 sizeof(unsigned long))
11403c77682dSLorenzo Stoakes static struct pfn_pool *current_pfn_pool __initdata;
11413c77682dSLorenzo Stoakes 
11423c77682dSLorenzo Stoakes static void __init __alloc_tag_add_early_pfn(unsigned long pfn)
11433c77682dSLorenzo Stoakes {
11443c77682dSLorenzo Stoakes 	struct pfn_pool *pool;
11453c77682dSLorenzo Stoakes 	int idx;
11463c77682dSLorenzo Stoakes 
11473c77682dSLorenzo Stoakes 	do {
11483c77682dSLorenzo Stoakes 		pool = READ_ONCE(current_pfn_pool);
11493c77682dSLorenzo Stoakes 		if (!pool || atomic_read(&pool->count) >= PFN_POOL_SIZE) {
11506372aac4SBrendan Jackman 			struct page *new_page = __alloc_pages(__GFP_HIGH, 0, numa_mem_id(),
11516372aac4SBrendan Jackman 							      NULL, ALLOC_NO_CODETAG);
11523c77682dSLorenzo Stoakes 			struct pfn_pool *new;
11533c77682dSLorenzo Stoakes 
11543c77682dSLorenzo Stoakes 			if (!new_page) {
11553c77682dSLorenzo Stoakes 				pr_warn_once("early PFN tracking page allocation failed\n");
11563c77682dSLorenzo Stoakes 				return;
11573c77682dSLorenzo Stoakes 			}
11583c77682dSLorenzo Stoakes 			new = page_address(new_page);
11593c77682dSLorenzo Stoakes 			new->next = pool;
11603c77682dSLorenzo Stoakes 			atomic_set(&new->count, 0);
11613c77682dSLorenzo Stoakes 			if (cmpxchg(&current_pfn_pool, pool, new) != pool) {
11623c77682dSLorenzo Stoakes 				clear_page_tag_ref(new_page);
11633c77682dSLorenzo Stoakes 				__free_page(new_page);
11643c77682dSLorenzo Stoakes 				continue;
11653c77682dSLorenzo Stoakes 			}
11663c77682dSLorenzo Stoakes 			pool = new;
11673c77682dSLorenzo Stoakes 		}
11683c77682dSLorenzo Stoakes 		idx = atomic_read(&pool->count);
11693c77682dSLorenzo Stoakes 		if (idx >= PFN_POOL_SIZE)
11703c77682dSLorenzo Stoakes 			continue;
11713c77682dSLorenzo Stoakes 		if (atomic_cmpxchg(&pool->count, idx, idx + 1) == idx)
11723c77682dSLorenzo Stoakes 			break;
11733c77682dSLorenzo Stoakes 	} while (1);
11743c77682dSLorenzo Stoakes 
11753c77682dSLorenzo Stoakes 	pool->pfns[idx] = pfn;
11763c77682dSLorenzo Stoakes }
11773c77682dSLorenzo Stoakes 
11783c77682dSLorenzo Stoakes typedef void alloc_tag_add_func(unsigned long pfn);
11793c77682dSLorenzo Stoakes static alloc_tag_add_func __rcu *alloc_tag_add_early_pfn_ptr __refdata =
11803c77682dSLorenzo Stoakes 	RCU_INITIALIZER(__alloc_tag_add_early_pfn);
11813c77682dSLorenzo Stoakes 
11826372aac4SBrendan Jackman void alloc_tag_add_early_pfn(unsigned long pfn, unsigned int alloc_flags)
11833c77682dSLorenzo Stoakes {
11843c77682dSLorenzo Stoakes 	alloc_tag_add_func *alloc_tag_add;
11853c77682dSLorenzo Stoakes 
11863c77682dSLorenzo Stoakes 	if (static_key_enabled(&mem_profiling_compressed))
11873c77682dSLorenzo Stoakes 		return;
11883c77682dSLorenzo Stoakes 
11893c77682dSLorenzo Stoakes 	/* Skip allocations for the tracking list itself to avoid recursion. */
11906372aac4SBrendan Jackman 	if (alloc_flags & ALLOC_NO_CODETAG)
11913c77682dSLorenzo Stoakes 		return;
11923c77682dSLorenzo Stoakes 
11933c77682dSLorenzo Stoakes 	rcu_read_lock();
11943c77682dSLorenzo Stoakes 	alloc_tag_add = rcu_dereference(alloc_tag_add_early_pfn_ptr);
11953c77682dSLorenzo Stoakes 	if (alloc_tag_add)
11963c77682dSLorenzo Stoakes 		alloc_tag_add(pfn);
11973c77682dSLorenzo Stoakes 	rcu_read_unlock();
11983c77682dSLorenzo Stoakes }
11993c77682dSLorenzo Stoakes 
12003c77682dSLorenzo Stoakes static void __init clear_early_alloc_pfn_tag_refs(void)
12013c77682dSLorenzo Stoakes {
12023c77682dSLorenzo Stoakes 	struct pfn_pool *pool, *next;
12033c77682dSLorenzo Stoakes 	struct page *page;
12043c77682dSLorenzo Stoakes 	int i;
12053c77682dSLorenzo Stoakes 
12063c77682dSLorenzo Stoakes 	if (static_key_enabled(&mem_profiling_compressed))
12073c77682dSLorenzo Stoakes 		return;
12083c77682dSLorenzo Stoakes 
12093c77682dSLorenzo Stoakes 	rcu_assign_pointer(alloc_tag_add_early_pfn_ptr, NULL);
12103c77682dSLorenzo Stoakes 	/* Make sure we are not racing with __alloc_tag_add_early_pfn() */
12113c77682dSLorenzo Stoakes 	synchronize_rcu();
12123c77682dSLorenzo Stoakes 
12133c77682dSLorenzo Stoakes 	for (pool = current_pfn_pool; pool; pool = next) {
12143c77682dSLorenzo Stoakes 		int nr_pfns = atomic_read(&pool->count);
12153c77682dSLorenzo Stoakes 
12163c77682dSLorenzo Stoakes 		for (i = 0; i < nr_pfns; i++) {
12173c77682dSLorenzo Stoakes 			unsigned long pfn = pool->pfns[i];
12183c77682dSLorenzo Stoakes 
12193c77682dSLorenzo Stoakes 			if (pfn_valid(pfn)) {
12203c77682dSLorenzo Stoakes 				union pgtag_ref_handle handle;
12213c77682dSLorenzo Stoakes 				union codetag_ref ref;
12223c77682dSLorenzo Stoakes 
12233c77682dSLorenzo Stoakes 				if (get_page_tag_ref(pfn_to_page(pfn), &ref, &handle)) {
12243c77682dSLorenzo Stoakes 					/*
12253c77682dSLorenzo Stoakes 					 * An early-allocated page could be freed and reallocated
12263c77682dSLorenzo Stoakes 					 * after its page_ext is initialized but before we clear it.
12273c77682dSLorenzo Stoakes 					 * In that case, it already has a valid tag set.
12283c77682dSLorenzo Stoakes 					 * We should not overwrite that valid tag
12293c77682dSLorenzo Stoakes 					 * with CODETAG_EMPTY.
12303c77682dSLorenzo Stoakes 					 *
12313c77682dSLorenzo Stoakes 					 * Note: there is still a small race window between checking
12323c77682dSLorenzo Stoakes 					 * ref.ct and calling set_codetag_empty(). We accept this
12333c77682dSLorenzo Stoakes 					 * race as it's unlikely and the extra complexity of atomic
12343c77682dSLorenzo Stoakes 					 * cmpxchg is not worth it for this debug-only code path.
12353c77682dSLorenzo Stoakes 					 */
12363c77682dSLorenzo Stoakes 					if (ref.ct) {
12373c77682dSLorenzo Stoakes 						put_page_tag_ref(handle);
12383c77682dSLorenzo Stoakes 						continue;
12393c77682dSLorenzo Stoakes 					}
12403c77682dSLorenzo Stoakes 
12413c77682dSLorenzo Stoakes 					set_codetag_empty(&ref);
12423c77682dSLorenzo Stoakes 					update_page_tag_ref(handle, &ref);
12433c77682dSLorenzo Stoakes 					put_page_tag_ref(handle);
12443c77682dSLorenzo Stoakes 				}
12453c77682dSLorenzo Stoakes 			}
12463c77682dSLorenzo Stoakes 		}
12473c77682dSLorenzo Stoakes 
12483c77682dSLorenzo Stoakes 		next = pool->next;
12493c77682dSLorenzo Stoakes 		page = virt_to_page(pool);
12503c77682dSLorenzo Stoakes 		clear_page_tag_ref(page);
12513c77682dSLorenzo Stoakes 		__free_page(page);
12523c77682dSLorenzo Stoakes 	}
12533c77682dSLorenzo Stoakes }
12543c77682dSLorenzo Stoakes #else /* !CONFIG_MEM_ALLOC_PROFILING_DEBUG */
12553c77682dSLorenzo Stoakes static inline void __init clear_early_alloc_pfn_tag_refs(void) {}
12563c77682dSLorenzo Stoakes #endif /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */
12573c77682dSLorenzo Stoakes 
12583c77682dSLorenzo Stoakes static __init void init_page_alloc_tagging(void)
12593c77682dSLorenzo Stoakes {
12603c77682dSLorenzo Stoakes 	clear_early_alloc_pfn_tag_refs();
12613c77682dSLorenzo Stoakes }
12623c77682dSLorenzo Stoakes 
12633c77682dSLorenzo Stoakes struct page_ext_operations page_alloc_tagging_ops = {
12643c77682dSLorenzo Stoakes 	.size = sizeof(union codetag_ref),
12653c77682dSLorenzo Stoakes 	.need = need_page_alloc_tagging,
12663c77682dSLorenzo Stoakes 	.init = init_page_alloc_tagging,
12673c77682dSLorenzo Stoakes };
12683c77682dSLorenzo Stoakes EXPORT_SYMBOL(page_alloc_tagging_ops);
12693c77682dSLorenzo Stoakes 
12703c77682dSLorenzo Stoakes #ifdef CONFIG_SYSCTL
12713c77682dSLorenzo Stoakes /*
12723c77682dSLorenzo Stoakes  * Not using proc_do_static_key() directly to prevent enabling profiling
12733c77682dSLorenzo Stoakes  * after it was shut down.
12743c77682dSLorenzo Stoakes  */
12753c77682dSLorenzo Stoakes static int proc_mem_profiling_handler(const struct ctl_table *table, int write,
12763c77682dSLorenzo Stoakes 				      void *buffer, size_t *lenp, loff_t *ppos)
12773c77682dSLorenzo Stoakes {
12783c77682dSLorenzo Stoakes 	if (write) {
12793c77682dSLorenzo Stoakes 		/*
12803c77682dSLorenzo Stoakes 		 * Call from do_sysctl_args() which is a no-op since the same
12813c77682dSLorenzo Stoakes 		 * value was already set by setup_early_mem_profiling.
12823c77682dSLorenzo Stoakes 		 * Return success to avoid warnings from do_sysctl_args().
12833c77682dSLorenzo Stoakes 		 */
12843c77682dSLorenzo Stoakes 		if (!current->mm)
12853c77682dSLorenzo Stoakes 			return 0;
12863c77682dSLorenzo Stoakes 
12873c77682dSLorenzo Stoakes #ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG
12883c77682dSLorenzo Stoakes 		/* User can't toggle profiling while debugging */
12893c77682dSLorenzo Stoakes 		return -EACCES;
12903c77682dSLorenzo Stoakes #endif
12913c77682dSLorenzo Stoakes 		if (!mem_profiling_support)
12923c77682dSLorenzo Stoakes 			return -EINVAL;
12933c77682dSLorenzo Stoakes 	}
12943c77682dSLorenzo Stoakes 
12953c77682dSLorenzo Stoakes 	return proc_do_static_key(table, write, buffer, lenp, ppos);
12963c77682dSLorenzo Stoakes }
12973c77682dSLorenzo Stoakes 
12983c77682dSLorenzo Stoakes 
12993c77682dSLorenzo Stoakes static const struct ctl_table memory_allocation_profiling_sysctls[] = {
13003c77682dSLorenzo Stoakes 	{
13013c77682dSLorenzo Stoakes 		.procname	= "mem_profiling",
13023c77682dSLorenzo Stoakes 		.data		= &mem_alloc_profiling_key,
13033c77682dSLorenzo Stoakes 		.mode		= 0644,
13043c77682dSLorenzo Stoakes 		.proc_handler	= proc_mem_profiling_handler,
13053c77682dSLorenzo Stoakes 	},
1306afff109cSAbhishek Bapat 	{
1307afff109cSAbhishek Bapat 		.procname	= "mem_profiling_compressed",
1308afff109cSAbhishek Bapat 		.data		= &mem_profiling_compressed,
1309afff109cSAbhishek Bapat 		.mode		= 0444,
1310afff109cSAbhishek Bapat 		.proc_handler	= proc_do_static_key,
1311afff109cSAbhishek Bapat 	},
13123c77682dSLorenzo Stoakes };
13133c77682dSLorenzo Stoakes 
13143c77682dSLorenzo Stoakes static void __init sysctl_init(void)
13153c77682dSLorenzo Stoakes {
13163c77682dSLorenzo Stoakes 	register_sysctl_init("vm", memory_allocation_profiling_sysctls);
13173c77682dSLorenzo Stoakes }
13183c77682dSLorenzo Stoakes #else /* CONFIG_SYSCTL */
13193c77682dSLorenzo Stoakes static inline void sysctl_init(void) {}
13203c77682dSLorenzo Stoakes #endif /* CONFIG_SYSCTL */
13213c77682dSLorenzo Stoakes 
13223c77682dSLorenzo Stoakes static int __init alloc_tag_init(void)
13233c77682dSLorenzo Stoakes {
13243c77682dSLorenzo Stoakes 	const struct codetag_type_desc desc = {
13253c77682dSLorenzo Stoakes 		.section		= ALLOC_TAG_SECTION_NAME,
13263c77682dSLorenzo Stoakes 		.tag_size		= sizeof(struct alloc_tag),
13273c77682dSLorenzo Stoakes #ifdef CONFIG_MODULES
13283c77682dSLorenzo Stoakes 		.needs_section_mem	= needs_section_mem,
13293c77682dSLorenzo Stoakes 		.alloc_section_mem	= reserve_module_tags,
13303c77682dSLorenzo Stoakes 		.free_section_mem	= release_module_tags,
13313c77682dSLorenzo Stoakes 		.module_load		= load_module,
13323c77682dSLorenzo Stoakes 		.module_replaced	= replace_module,
13333c77682dSLorenzo Stoakes #endif
13343c77682dSLorenzo Stoakes 	};
13353c77682dSLorenzo Stoakes 	int res;
13363c77682dSLorenzo Stoakes 
13373c77682dSLorenzo Stoakes 	sysctl_init();
13383c77682dSLorenzo Stoakes 
13393c77682dSLorenzo Stoakes 	if (!mem_profiling_support) {
13403c77682dSLorenzo Stoakes 		pr_info("Memory allocation profiling is not supported!\n");
13413c77682dSLorenzo Stoakes 		return 0;
13423c77682dSLorenzo Stoakes 	}
13433c77682dSLorenzo Stoakes 
13441d581ab2SSuren Baghdasaryan 	if (!proc_create(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_proc_ops)) {
13453c77682dSLorenzo Stoakes 		pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
13463c77682dSLorenzo Stoakes 		shutdown_mem_profiling(false);
13473c77682dSLorenzo Stoakes 		return -ENOMEM;
13483c77682dSLorenzo Stoakes 	}
13493c77682dSLorenzo Stoakes 
13503c77682dSLorenzo Stoakes 	res = alloc_mod_tags_mem();
13513c77682dSLorenzo Stoakes 	if (res) {
13523c77682dSLorenzo Stoakes 		pr_err("Failed to reserve address space for module tags, errno = %d\n", res);
13533c77682dSLorenzo Stoakes 		shutdown_mem_profiling(true);
13543c77682dSLorenzo Stoakes 		return res;
13553c77682dSLorenzo Stoakes 	}
13563c77682dSLorenzo Stoakes 
13573c77682dSLorenzo Stoakes 	alloc_tag_cttype = codetag_register_type(&desc);
13583c77682dSLorenzo Stoakes 	if (IS_ERR(alloc_tag_cttype)) {
13593c77682dSLorenzo Stoakes 		pr_err("Allocation tags registration failed, errno = %pe\n", alloc_tag_cttype);
13603c77682dSLorenzo Stoakes 		free_mod_tags_mem();
13613c77682dSLorenzo Stoakes 		shutdown_mem_profiling(true);
13623c77682dSLorenzo Stoakes 		return PTR_ERR(alloc_tag_cttype);
13633c77682dSLorenzo Stoakes 	}
13643c77682dSLorenzo Stoakes 
13653c77682dSLorenzo Stoakes 	return 0;
13663c77682dSLorenzo Stoakes }
13673c77682dSLorenzo Stoakes module_init(alloc_tag_init);
1368