From 0414f5d0d3c39646172bd33399e1c8b9f1817094 Mon Sep 17 00:00:00 2001 From: Faiyaz Mohammed Date: Mon, 10 Feb 2020 19:02:17 +0530 Subject: [PATCH] mm: slub: Add debugfs interface to capture slub allocs owner With kmem event we were missing number of allocs and frees due to high frequency of event occurrence. So added debugfs interface to capture the slub alloc owners from slab list and it prints data using sequential file interface when we execute the "cat /sys/kernel/debug/slab/alloc_trace" command. Change-Id: I72eb5a21cc20a046f04d88d4dfbafa5cf378805a Signed-off-by: Faiyaz Mohammed --- mm/slub.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) 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;