* [PATCH 1/2] tools/mm/slabinfo: refactor slab attribute reading
2026-09-27 5:57 [PATCH 0/2] tools/mm/slabinfo: report sheaf and barn statistics Seongjun Hong
@ 2026-09-27 5:57 ` Seongjun Hong
2026-09-28 15:52 ` Harry Yoo
2026-09-27 5:57 ` [PATCH 2/2] tools/mm/slabinfo: report percpu sheaves and barn statistics Seongjun Hong
1 sibling, 1 reply; 5+ messages in thread
From: Seongjun Hong @ 2026-09-27 5:57 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton, Hao Li,
Christoph Lameter, David Rientjes, Roman Gushchin
Cc: linux-mm, linux-kernel, Seongjun Hong
The fields of struct slabinfo were mixed with deprecated sysfs files,
which looks complicated. Organize every field of struct slabinfo by
config option and the attribute order in mm/slub.c's slab_attrs[]. Because
slabinfo should be able to run on previous kernel releases, leave the
deprecated members for this time.
read_slab_dir() had ~50 lines of attribute reads inlined. Move them to a
new fill_slabinfo() helper so that directory walk and per-cache
attribute reading are separated.
Fold get_obj_and_str() and decode_numa_list() into get_obj_and_decode()
to avoid unnecessary strdup() and leave only one member assignment in
fill_slabinfo() for each sysfs file.
Make broken statements into a single line for better readibility.
Remove alias field from struct slabinfo because it is initialized to 0
and never updated. The only usage of this field "if (slab->alias)" is dead.
No functional change intended.
Signed-off-by: Seongjun Hong <hsj0512@snu.ac.kr>
---
tools/mm/slabinfo.c | 245 ++++++++++++++++++++++++++++------------------------
1 file changed, 131 insertions(+), 114 deletions(-)
diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c
index 48d1ee8b0e81..84359d628f2e 100644
--- a/tools/mm/slabinfo.c
+++ b/tools/mm/slabinfo.c
@@ -27,25 +27,49 @@
struct slabinfo {
char *name;
- int alias;
int refs;
- int aliases, align, cache_dma, cpu_slabs, destroy_by_rcu;
- unsigned int hwcache_align, object_size, objs_per_slab;
- unsigned int sanity_checks, slab_size, store_user, trace;
- int order, poison, reclaim_account, red_zone;
- unsigned long partial, objects, slabs, objects_partial, total_objects;
+ int numa_slabs[MAX_NODES];
+ int numa_partial[MAX_NODES];
+
+ unsigned int slab_size, object_size;
+ unsigned int objs_per_slab, order;
+ unsigned long objects_partial, partial;
+ int aliases;
+ unsigned int align;
+ int hwcache_align;
+ int reclaim_account;
+ int destroy_by_rcu;
+
+ /* CONFIG_SLUB_DEBUG */
+ unsigned long total_objects, objects, slabs;
+ int sanity_checks, trace, red_zone, poison, store_user;
+
+ /* CONFIG_ZONE_DMA */
+ int cache_dma;
+
+ /* CONFIG_SLUB_STATS */
unsigned long alloc_fastpath, alloc_slowpath;
unsigned long free_fastpath, free_slowpath;
- unsigned long free_frozen, free_add_partial, free_remove_partial;
- unsigned long alloc_from_partial, alloc_slab, free_slab, alloc_refill;
- unsigned long cpuslab_flush, deactivate_full, deactivate_empty;
+ unsigned long free_add_partial, free_remove_partial;
+ unsigned long alloc_slab, alloc_node_mismatch, free_slab;
+ unsigned long order_fallback;
+ unsigned long cmpxchg_double_fail;
+
+ /*
+ * Deprecated files:
+ * No STAT_ATTR()/SLAB_ATTR() for these exists in mm/slub.c's
+ * slab_attrs[] anymore. Although cpu_slabs remains as a file,
+ * it is also outdated and always prints 0. Keep these for backward
+ * compatibility, but they should be removed later.
+ */
+ int cpu_slabs;
+ unsigned long free_frozen;
unsigned long deactivate_to_head, deactivate_to_tail;
- unsigned long deactivate_remote_frees, order_fallback;
- unsigned long cmpxchg_double_cpu_fail, cmpxchg_double_fail;
- unsigned long alloc_node_mismatch, deactivate_bypass;
+ unsigned long alloc_from_partial, alloc_refill;
+ unsigned long cpuslab_flush, deactivate_full, deactivate_empty;
+ unsigned long deactivate_remote_frees, deactivate_bypass;
+ unsigned long cmpxchg_double_cpu_fail;
unsigned long cpu_partial_alloc, cpu_partial_free;
- int numa[MAX_NODES];
- int numa_partial[MAX_NODES];
} slabinfo[MAX_SLABS];
struct aliasinfo {
@@ -174,6 +198,30 @@ static unsigned long read_obj(const char *name)
return strlen(buffer);
}
+static void decode_numa_list(int *numa, char *t)
+{
+ int node;
+ int nr;
+
+ memset(numa, 0, MAX_NODES * sizeof(int));
+
+ if (!t)
+ return;
+
+ while (*t == 'N') {
+ t++;
+ node = strtoul(t, &t, 10);
+ if (*t == '=') {
+ t++;
+ nr = strtoul(t, &t, 10);
+ numa[node] = nr;
+ if (node > highest_node)
+ highest_node = node;
+ }
+ while (*t == ' ')
+ t++;
+ }
+}
/*
* Get the contents of an attribute
@@ -186,21 +234,19 @@ static unsigned long get_obj(const char *name)
return atol(buffer);
}
-static unsigned long get_obj_and_str(const char *name, char **x)
+static unsigned long get_obj_and_decode(int *numa, const char *name)
{
unsigned long result = 0;
char *p;
- *x = NULL;
-
if (!read_obj(name))
return 0;
result = strtoul(buffer, &p, 10);
while (*p == ' ')
p++;
- if (*p)
- *x = strdup(p);
+
+ decode_numa_list(numa, p);
return result;
}
@@ -293,31 +339,6 @@ static int store_size(char *buffer, unsigned long value)
return n;
}
-static void decode_numa_list(int *numa, char *t)
-{
- int node;
- int nr;
-
- memset(numa, 0, MAX_NODES * sizeof(int));
-
- if (!t)
- return;
-
- while (*t == 'N') {
- t++;
- node = strtoul(t, &t, 10);
- if (*t == '=') {
- t++;
- nr = strtoul(t, &t, 10);
- numa[node] = nr;
- if (node > highest_node)
- highest_node = node;
- }
- while (*t == ' ')
- t++;
- }
-}
-
static void slab_validate(struct slabinfo *s)
{
if (strcmp(s->name, "*") == 0)
@@ -410,7 +431,7 @@ static void slab_numa(struct slabinfo *s, int mode)
for(node = 0; node <= highest_node; node++) {
char b[20];
- store_size(b, s->numa[node]);
+ store_size(b, s->numa_slabs[node]);
printf(" %4s", b);
}
printf("\n");
@@ -1210,6 +1231,57 @@ static int slab_mismatch(char *slab)
return regexec(&pattern, slab, 0, NULL, 0);
}
+static void fill_slabinfo(struct slabinfo *slab, const char *name)
+{
+ slab->name = strdup(name);
+ slab->refs = 0;
+ slab->aliases = get_obj("aliases");
+ slab->align = get_obj("align");
+ slab->cache_dma = get_obj("cache_dma");
+ slab->cpu_slabs = get_obj("cpu_slabs");
+ slab->destroy_by_rcu = get_obj("destroy_by_rcu");
+ slab->hwcache_align = get_obj("hwcache_align");
+ slab->object_size = get_obj("object_size");
+ slab->objects = get_obj("objects");
+ slab->objects_partial = get_obj("objects_partial");
+ slab->total_objects = get_obj("total_objects");
+ slab->objs_per_slab = get_obj("objs_per_slab");
+ slab->order = get_obj("order");
+ slab->partial = get_obj_and_decode(slab->numa_partial, "partial");
+ slab->poison = get_obj("poison");
+ slab->reclaim_account = get_obj("reclaim_account");
+ slab->red_zone = get_obj("red_zone");
+ slab->sanity_checks = get_obj("sanity_checks");
+ slab->slab_size = get_obj("slab_size");
+ slab->slabs = get_obj_and_decode(slab->numa_slabs, "slabs");
+ slab->store_user = get_obj("store_user");
+ slab->trace = get_obj("trace");
+ slab->alloc_fastpath = get_obj("alloc_fastpath");
+ slab->alloc_slowpath = get_obj("alloc_slowpath");
+ slab->free_fastpath = get_obj("free_fastpath");
+ slab->free_slowpath = get_obj("free_slowpath");
+ slab->free_frozen = get_obj("free_frozen");
+ slab->free_add_partial = get_obj("free_add_partial");
+ slab->free_remove_partial = get_obj("free_remove_partial");
+ slab->alloc_from_partial = get_obj("alloc_from_partial");
+ slab->alloc_slab = get_obj("alloc_slab");
+ slab->alloc_refill = get_obj("alloc_refill");
+ slab->free_slab = get_obj("free_slab");
+ slab->cpuslab_flush = get_obj("cpuslab_flush");
+ slab->deactivate_full = get_obj("deactivate_full");
+ slab->deactivate_empty = get_obj("deactivate_empty");
+ slab->deactivate_to_head = get_obj("deactivate_to_head");
+ slab->deactivate_to_tail = get_obj("deactivate_to_tail");
+ slab->deactivate_remote_frees = get_obj("deactivate_remote_frees");
+ slab->order_fallback = get_obj("order_fallback");
+ slab->cmpxchg_double_cpu_fail = get_obj("cmpxchg_double_cpu_fail");
+ slab->cmpxchg_double_fail = get_obj("cmpxchg_double_fail");
+ slab->cpu_partial_alloc = get_obj("cpu_partial_alloc");
+ slab->cpu_partial_free = get_obj("cpu_partial_free");
+ slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");
+ slab->deactivate_bypass = get_obj("deactivate_bypass");
+}
+
static void read_slab_dir(void)
{
DIR *dir;
@@ -1217,7 +1289,6 @@ static void read_slab_dir(void)
struct slabinfo *slab = slabinfo;
struct aliasinfo *alias = aliasinfo;
char *p;
- char *t;
int count;
if (chdir("/sys/kernel/slab") && chdir("/sys/slab"))
@@ -1226,14 +1297,14 @@ static void read_slab_dir(void)
dir = opendir(".");
while ((de = readdir(dir))) {
if (de->d_name[0] == '.' ||
- (de->d_name[0] != ':' && slab_mismatch(de->d_name)))
- continue;
+ (de->d_name[0] != ':' && slab_mismatch(de->d_name)))
+ continue;
switch (de->d_type) {
- case DT_LNK:
+ case DT_LNK:
if (alias - aliasinfo == MAX_ALIASES)
fatal("Too many aliases\n");
alias->name = strdup(de->d_name);
- count = readlink(de->d_name, buffer, sizeof(buffer)-1);
+ count = readlink(de->d_name, buffer, sizeof(buffer) - 1);
if (count < 0)
fatal("Cannot read symlink %s\n", de->d_name);
@@ -1245,71 +1316,21 @@ static void read_slab_dir(void)
alias->ref = strdup(p);
alias++;
break;
- case DT_DIR:
+ case DT_DIR:
if (slab - slabinfo == MAX_SLABS)
fatal("Too many slabs\n");
if (chdir(de->d_name))
fatal("Unable to access slab %s\n", slab->name);
- slab->name = strdup(de->d_name);
- slab->alias = 0;
- slab->refs = 0;
- slab->aliases = get_obj("aliases");
- slab->align = get_obj("align");
- slab->cache_dma = get_obj("cache_dma");
- slab->cpu_slabs = get_obj("cpu_slabs");
- slab->destroy_by_rcu = get_obj("destroy_by_rcu");
- slab->hwcache_align = get_obj("hwcache_align");
- slab->object_size = get_obj("object_size");
- slab->objects = get_obj("objects");
- slab->objects_partial = get_obj("objects_partial");
- slab->total_objects = get_obj("total_objects");
- slab->objs_per_slab = get_obj("objs_per_slab");
- slab->order = get_obj("order");
- slab->partial = get_obj_and_str("partial", &t);
- decode_numa_list(slab->numa_partial, t);
- free(t);
- slab->poison = get_obj("poison");
- slab->reclaim_account = get_obj("reclaim_account");
- slab->red_zone = get_obj("red_zone");
- slab->sanity_checks = get_obj("sanity_checks");
- slab->slab_size = get_obj("slab_size");
- slab->slabs = get_obj_and_str("slabs", &t);
- decode_numa_list(slab->numa, t);
- free(t);
- slab->store_user = get_obj("store_user");
- slab->trace = get_obj("trace");
- slab->alloc_fastpath = get_obj("alloc_fastpath");
- slab->alloc_slowpath = get_obj("alloc_slowpath");
- slab->free_fastpath = get_obj("free_fastpath");
- slab->free_slowpath = get_obj("free_slowpath");
- slab->free_frozen= get_obj("free_frozen");
- slab->free_add_partial = get_obj("free_add_partial");
- slab->free_remove_partial = get_obj("free_remove_partial");
- slab->alloc_from_partial = get_obj("alloc_from_partial");
- slab->alloc_slab = get_obj("alloc_slab");
- slab->alloc_refill = get_obj("alloc_refill");
- slab->free_slab = get_obj("free_slab");
- slab->cpuslab_flush = get_obj("cpuslab_flush");
- slab->deactivate_full = get_obj("deactivate_full");
- slab->deactivate_empty = get_obj("deactivate_empty");
- slab->deactivate_to_head = get_obj("deactivate_to_head");
- slab->deactivate_to_tail = get_obj("deactivate_to_tail");
- slab->deactivate_remote_frees = get_obj("deactivate_remote_frees");
- slab->order_fallback = get_obj("order_fallback");
- slab->cmpxchg_double_cpu_fail = get_obj("cmpxchg_double_cpu_fail");
- slab->cmpxchg_double_fail = get_obj("cmpxchg_double_fail");
- slab->cpu_partial_alloc = get_obj("cpu_partial_alloc");
- slab->cpu_partial_free = get_obj("cpu_partial_free");
- slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");
- slab->deactivate_bypass = get_obj("deactivate_bypass");
+
+ fill_slabinfo(slab, de->d_name);
+
if (chdir(".."))
- fatal("Unable to chdir from slab ../%s\n",
- slab->name);
+ fatal("Unable to chdir from slab ../%s\n", slab->name);
if (slab->name[0] == ':')
alias_targets++;
slab++;
break;
- default :
+ default:
fatal("Unknown file type %lx\n", de->d_type);
}
}
@@ -1324,12 +1345,7 @@ static void output_slabs(void)
struct slabinfo *slab;
int lines = output_lines;
- for (slab = slabinfo; (slab < slabinfo + slabs) &&
- lines != 0; slab++) {
-
- if (slab->alias)
- continue;
-
+ for (slab = slabinfo; (slab < slabinfo + slabs) && lines != 0; slab++) {
if (lines != -1)
lines--;
@@ -1528,9 +1544,10 @@ int main(int argc, char *argv[])
err = regcomp(&pattern, pattern_source, REG_ICASE|REG_NOSUB);
if (err)
- fatal("%s: Invalid pattern '%s' code %d\n",
- argv[0], pattern_source, err);
+ fatal("%s: Invalid pattern '%s' code %d\n", argv[0], pattern_source, err);
+
read_slab_dir();
+
if (show_alias) {
alias();
} else if (extended_totals) {
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/2] tools/mm/slabinfo: report percpu sheaves and barn statistics
2026-09-27 5:57 [PATCH 0/2] tools/mm/slabinfo: report sheaf and barn statistics Seongjun Hong
2026-09-27 5:57 ` [PATCH 1/2] tools/mm/slabinfo: refactor slab attribute reading Seongjun Hong
@ 2026-09-27 5:57 ` Seongjun Hong
2026-09-28 16:00 ` Harry Yoo
1 sibling, 1 reply; 5+ messages in thread
From: Seongjun Hong @ 2026-09-27 5:57 UTC (permalink / raw)
To: Vlastimil Babka, Harry Yoo, Andrew Morton, Hao Li,
Christoph Lameter, David Rientjes, Roman Gushchin
Cc: linux-mm, linux-kernel, Seongjun Hong
Since commit 2d517aa09bbc ("slab: add opt-in caching layer of percpu
sheaves") sysfs provides the statistics of sheaf and barn, but slabinfo
did not utilize this information. Display perf, API, and sheaf allocation
counters on slabinfo report(-r).
Example slabinfo report output:
$ slabinfo -r kmalloc-1k
Sheaf capacity 12 objects
Sheaf Perf Counter Success Fail
--------------------------------------------------
Barn get 4 43
Barn put 4 0
RCU sheaf free 34 0
Sheaf API Counter Fast Slow
--------------------------------------------------
Prefill 0 0
Return 0 0
Sheaf Objects/Allocations
--------------------------------------------------
Objects flushed to slabs 0
Objects refilled into sheaves 516
Sheaves allocated 7
Sheaves freed 0
Oversize sheaves for prefill 0
Signed-off-by: Seongjun Hong <hsj0512@snu.ac.kr>
---
tools/mm/slabinfo.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c
index 84359d628f2e..dbc72582ffab 100644
--- a/tools/mm/slabinfo.c
+++ b/tools/mm/slabinfo.c
@@ -33,6 +33,7 @@ struct slabinfo {
unsigned int slab_size, object_size;
unsigned int objs_per_slab, order;
+ unsigned int sheaf_capacity;
unsigned long objects_partial, partial;
int aliases;
unsigned int align;
@@ -49,12 +50,19 @@ struct slabinfo {
/* CONFIG_SLUB_STATS */
unsigned long alloc_fastpath, alloc_slowpath;
+ unsigned long free_rcu_sheaf, free_rcu_sheaf_fail;
unsigned long free_fastpath, free_slowpath;
unsigned long free_add_partial, free_remove_partial;
unsigned long alloc_slab, alloc_node_mismatch, free_slab;
unsigned long order_fallback;
unsigned long cmpxchg_double_fail;
+ unsigned long sheaf_flush, sheaf_refill, sheaf_alloc, sheaf_free;
+ unsigned long barn_get, barn_get_fail, barn_put, barn_put_fail;
+ unsigned long sheaf_prefill_fast, sheaf_prefill_slow,
+ sheaf_prefill_oversize;
+ unsigned long sheaf_return_fast, sheaf_return_slow;
+
/*
* Deprecated files:
* No STAT_ATTR()/SLAB_ATTR() for these exists in mm/slub.c's
@@ -574,6 +582,39 @@ static void slab_stats(struct slabinfo *s)
}
}
+static void sheaf_stats(struct slabinfo *s)
+{
+ if (!s->sheaf_capacity)
+ return;
+
+ printf("\nSheaf capacity %8u objects\n", s->sheaf_capacity);
+
+ printf("\nSheaf Perf Counter Success Fail\n");
+ printf("--------------------------------------------------\n");
+ printf("Barn get %8lu %8lu\n",
+ s->barn_get, s->barn_get_fail);
+ printf("Barn put %8lu %8lu\n",
+ s->barn_put, s->barn_put_fail);
+ printf("RCU sheaf free %8lu %8lu\n",
+ s->free_rcu_sheaf, s->free_rcu_sheaf_fail);
+
+ printf("\nSheaf API Counter Fast Slow\n");
+ printf("--------------------------------------------------\n");
+ printf("Prefill %8lu %8lu\n",
+ s->sheaf_prefill_fast, s->sheaf_prefill_slow);
+ printf("Return %8lu %8lu\n",
+ s->sheaf_return_fast, s->sheaf_return_slow);
+
+ printf("\nSheaf Objects/Allocations\n");
+ printf("--------------------------------------------------\n");
+ printf("Objects flushed to slabs %8lu\n", s->sheaf_flush);
+ printf("Objects refilled into sheaves %8lu\n", s->sheaf_refill);
+ printf("Sheaves allocated %8lu\n", s->sheaf_alloc);
+ printf("Sheaves freed %8lu\n", s->sheaf_free);
+ printf("Oversize sheaves for prefill %8lu\n",
+ s->sheaf_prefill_oversize);
+}
+
static void report(struct slabinfo *s)
{
if (strcmp(s->name, "*") == 0)
@@ -613,6 +654,7 @@ static void report(struct slabinfo *s)
show_tracking(s);
slab_numa(s, 1);
slab_stats(s);
+ sheaf_stats(s);
}
static void slabcache(struct slabinfo *s)
@@ -1280,6 +1322,22 @@ static void fill_slabinfo(struct slabinfo *slab, const char *name)
slab->cpu_partial_free = get_obj("cpu_partial_free");
slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");
slab->deactivate_bypass = get_obj("deactivate_bypass");
+ slab->sheaf_capacity = get_obj("sheaf_capacity");
+ slab->free_rcu_sheaf = get_obj("free_rcu_sheaf");
+ slab->free_rcu_sheaf_fail = get_obj("free_rcu_sheaf_fail");
+ slab->sheaf_flush = get_obj("sheaf_flush");
+ slab->sheaf_refill = get_obj("sheaf_refill");
+ slab->sheaf_alloc = get_obj("sheaf_alloc");
+ slab->sheaf_free = get_obj("sheaf_free");
+ slab->barn_get = get_obj("barn_get");
+ slab->barn_get_fail = get_obj("barn_get_fail");
+ slab->barn_put = get_obj("barn_put");
+ slab->barn_put_fail = get_obj("barn_put_fail");
+ slab->sheaf_prefill_fast = get_obj("sheaf_prefill_fast");
+ slab->sheaf_prefill_slow = get_obj("sheaf_prefill_slow");
+ slab->sheaf_prefill_oversize = get_obj("sheaf_prefill_oversize");
+ slab->sheaf_return_fast = get_obj("sheaf_return_fast");
+ slab->sheaf_return_slow = get_obj("sheaf_return_slow");
}
static void read_slab_dir(void)
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread