Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac1e0fa5 authored by Faiyaz Mohammed's avatar Faiyaz Mohammed Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarFaiyaz Mohammed <faiyazm@codeaurora.org>
parent e11d06a5
Loading
Loading
Loading
Loading
+99 −0
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@

#include <trace/events/kmem.h>

#ifdef CONFIG_SLUB_DEBUG
#include <linux/debugfs.h>
#endif

#include "internal.h"

/*
@@ -5894,6 +5898,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;
@@ -5940,6 +6023,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;