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 <faiyazm@codeaurora.org>
This commit is contained in:
Faiyaz Mohammed 2020-02-10 19:02:17 +05:30
commit 0414f5d0d3

View file

@ -37,6 +37,10 @@
#include <trace/events/kmem.h>
#ifdef CONFIG_SLUB_DEBUG
#include <linux/debugfs.h>
#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;