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, ¶ms, 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(¶ms, 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, ¶ms.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, ¶ms, 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(¶ms, 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, ¶ms, &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, ¶ms, 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(¤t_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