diff --git a/mm/slub.c b/mm/slub.c index 0b973d1f89b1..562674141add 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -37,6 +37,10 @@ #include +#ifdef CONFIG_SLUB_DEBUG +#include +#endif + #include "internal.h" /* @@ -5910,6 +5914,85 @@ struct saved_alias { static struct saved_alias *alias_list; +#ifdef CONFIG_SLUB_DEBUG +static struct dentry *slab_debugfs_top; + +static int alloc_trace_locations(struct seq_file *seq, struct kmem_cache *s, + enum track_item alloc) +{ + unsigned long i; + struct loc_track t = { 0, 0, NULL }; + int node; + unsigned long *map = kmalloc(BITS_TO_LONGS(oo_objects(s->max)) * + sizeof(unsigned long), GFP_KERNEL); + struct kmem_cache_node *n; + + if (!map || !alloc_loc_track(&t, PAGE_SIZE / sizeof(struct location), + GFP_KERNEL)) { + kfree(map); + return -ENOMEM; + } + /* Push back cpu slabs */ + flush_all(s); + + for_each_kmem_cache_node(s, node, n) { + unsigned long flags; + struct page *page; + + if (!atomic_long_read(&n->nr_slabs)) + continue; + + spin_lock_irqsave(&n->list_lock, flags); + list_for_each_entry(page, &n->partial, lru) + process_slab(&t, s, page, alloc, map); + list_for_each_entry(page, &n->full, lru) + process_slab(&t, s, page, alloc, map); + spin_unlock_irqrestore(&n->list_lock, flags); + } + + for (i = 0; i < t.count; i++) { + struct location *l = &t.loc[i]; + + seq_printf(seq, + "alloc_list: call_site=%pS count=%zu object_size=%zu slab_size=%zu slab_name=%s\n", + l->addr, l->count, s->object_size, s->size, s->name); + } + + free_loc_track(&t); + kfree(map); + return 0; +} + +static int slab_debug_alloc_trace(struct seq_file *seq, + void *ignored) +{ + + struct kmem_cache *slab; + + list_for_each_entry(slab, &slab_caches, list) { + if (!(slab->flags & SLAB_STORE_USER)) + continue; + alloc_trace_locations(seq, slab, TRACK_ALLOC); + } + + return 0; +} + +static int slab_debug_alloc_trace_open(struct inode *inode, + struct file *file) +{ + return single_open(file, slab_debug_alloc_trace, + inode->i_private); +} + +static const struct file_operations slab_debug_alloc_fops = { + .open = slab_debug_alloc_trace_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + static int sysfs_slab_alias(struct kmem_cache *s, const char *name) { struct saved_alias *al; @@ -5956,6 +6039,22 @@ static int __init slab_sysfs_init(void) s->name); } +#ifdef CONFIG_SLUB_DEBUG + if (slub_debug) { + slab_debugfs_top = debugfs_create_dir("slab", NULL); + if (!slab_debugfs_top) { + pr_err("Couldn't create slab debugfs directory\n"); + return -ENODEV; + } + + if (!debugfs_create_file("alloc_trace", 0400, slab_debugfs_top, + NULL, &slab_debug_alloc_fops)) { + pr_err("Couldn't create slab/tests debugfs directory\n"); + return -ENODEV; + } + } +#endif + while (alias_list) { struct saved_alias *al = alias_list;