Hi Harshvardhan,
On Mon, Aug 17, 2026 at 12:02 AM Harshvardhan Jha
<harshvardhan.j.jha(a)oracle.com> wrote:
Linux commit 5ba6bc27b1f9 ("slab: decouple pointer to barn from
kmem_cache_node") moved the kmem_cache_node pointers from
kmem_cache.node[] to kmem_cache.per_node[].node.
Teach the SLUB traversal code to recognize and handle both layouts.
Without this, crash treats the new layout as non-NUMA and kmem -s fails
while looking for the removed kmem_cache.local_node member.
Signed-off-by: Harshvardhan Jha <harshvardhan.j.jha(a)oracle.com>
---
Testing was performed manually on x86_64 VMs:
- Linux 7.1.0-rc7 with the new per_node layout: upstream crash failed
with the kmem_cache_local_node invalid-offset error, while the patched
crash completed kmem -s and displayed the slab-cache table.
- Linux 6.18.0-rc7 with the old node[1024] layout: the patched crash
completed kmem -s and displayed the slab-cache table.
defs.h | 3 +++
memory.c | 39 ++++++++++++++++++++++++++++++---------
symbols.c | 6 ++++++
3 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/defs.h b/defs.h
index e3027e2..ccd84a0 100644
--- a/defs.h
+++ b/defs.h
@@ -1766,6 +1766,8 @@ struct offset_table { /* stash of commonly-used
offsets */
long kmem_cache_name;
long kmem_cache_list;
long kmem_cache_node;
+ long kmem_cache_per_node;
+ long kmem_cache_per_node_node;
long kmem_cache_cpu_slab;
long page_inuse;
/* long page_offset; use "old" page->offset */
@@ -2395,6 +2397,7 @@ struct size_table { /* stash of commonly-used sizes */
long rlimit;
long kmem_cache;
long kmem_cache_node;
+ long kmem_cache_per_node_ptrs;
The offset_table & size_table, any new added members should be
appended at the end of the struct, not in the middle. Please check
https://github.com/crash-utility/crash/wiki#writing-patches
long upid;
long kmem_cache_cpu;
long cfs_rq;
diff --git a/memory.c b/memory.c
index 3f3aa27..cf26bd4 100644
--- a/memory.c
+++ b/memory.c
@@ -306,6 +306,7 @@ static int get_kmem_cache_list(ulong **);
static int get_kmem_cache_root_list(ulong **);
static int get_kmem_cache_child_list(ulong **, ulong);
static int get_kmem_cache_slub_data(long, struct meminfo *);
+static ulong get_kmem_cache_node_ptr(struct meminfo *, int);
static ulong compound_head(ulong);
static long count_partial(ulong, struct meminfo *, ulong *);
static short count_cpu_partial(struct meminfo *, int);
@@ -869,6 +870,9 @@ vm_init(void)
MEMBER_OFFSET_INIT(kmem_cache_inuse, "kmem_cache",
"inuse");
MEMBER_OFFSET_INIT(kmem_cache_align, "kmem_cache",
"align");
MEMBER_OFFSET_INIT(kmem_cache_node, "kmem_cache",
"node");
+ MEMBER_OFFSET_INIT(kmem_cache_per_node, "kmem_cache",
"per_node");
+ MEMBER_OFFSET_INIT(kmem_cache_per_node_node,
+ "kmem_cache_per_node_ptrs", "node");
MEMBER_OFFSET_INIT(kmem_cache_cpu_slab, "kmem_cache",
"cpu_slab");
MEMBER_OFFSET_INIT(kmem_cache_list, "kmem_cache",
"list");
MEMBER_OFFSET_INIT(kmem_cache_red_left_pad, "kmem_cache",
"red_left_pad");
@@ -920,9 +924,16 @@ vm_init(void)
if (INVALID_MEMBER(page_objects))
ANON_MEMBER_OFFSET_INIT(page_objects, "slab",
"objects");
}
+ STRUCT_SIZE_INIT(kmem_cache_per_node_ptrs,
+ "kmem_cache_per_node_ptrs");
if (VALID_MEMBER(kmem_cache_node)) {
ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.node", NULL,
0);
vt->flags |= CONFIG_NUMA;
+ } else if (VALID_MEMBER(kmem_cache_per_node) &&
+ VALID_MEMBER(kmem_cache_per_node_node) &&
+ VALID_SIZE(kmem_cache_per_node_ptrs)) {
+ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.per_node",
NULL, 0);
+ vt->flags |= CONFIG_NUMA;
}
ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.cpu_slab", NULL, 0);
@@ -19460,6 +19471,22 @@ count_cpu_partial(struct meminfo *si, int cpu)
return free_objects;
}
+static ulong
+get_kmem_cache_node_ptr(struct meminfo *si, int node)
+{
+ char *node_addr;
+
+ if (VALID_MEMBER(kmem_cache_node))
+ node_addr = si->cache_buf + OFFSET(kmem_cache_node) +
+ (sizeof(void *) * node);
+ else
+ node_addr = si->cache_buf + OFFSET(kmem_cache_per_node) +
+ (SIZE(kmem_cache_per_node_ptrs) * node) +
+ OFFSET(kmem_cache_per_node_node);
I suggest making the old approach the default (else) branch and the
new approach the if (condition) branch:
if (VALID_MEMBER(kmem_cache_per_node) &&
VALID_MEMBER(kmem_cache_per_node_node)) {
node_addr = si->cache_buf + OFFSET(kmem_cache_per_node) +
(SIZE(kmem_cache_per_node_ptrs) * node) +
OFFSET(kmem_cache_per_node_node);
} else {
node_addr = si->cache_buf + OFFSET(kmem_cache_node) +
(sizeof(void *) * node);
}
Since we have used the old branch for a longer time, I'd prefer
anything that doesn't match the new condition to fall into the old
one.
Thanks,
Tao Liu
+
+ return ULONG(node_addr);
+}
+
/*
* Emulate the total count calculation done by the
* slab_objects() sysfs function in slub.c.
@@ -19537,9 +19564,7 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si)
for (n = 0; n < vt->numnodes; n++) {
if (vt->flags & CONFIG_NUMA) {
nt = &vt->node_table[n];
- node_ptr = ULONG(si->cache_buf +
- OFFSET(kmem_cache_node) +
- (sizeof(void *) * nt->node_id));
+ node_ptr = get_kmem_cache_node_ptr(si, nt->node_id);
} else
node_ptr = si->cache +
OFFSET(kmem_cache_local_node);
@@ -19709,9 +19734,7 @@ do_kmem_cache_slub(struct meminfo *si)
for (n = 0; n < vt->numnodes; n++) {
if (vt->flags & CONFIG_NUMA) {
nt = &vt->node_table[n];
- node_ptr = ULONG(si->cache_buf +
- OFFSET(kmem_cache_node) +
- (sizeof(void *)* nt->node_id));
+ node_ptr = get_kmem_cache_node_ptr(si, nt->node_id);
} else
node_ptr = si->cache +
OFFSET(kmem_cache_local_node);
@@ -20515,9 +20538,7 @@ slab_to_kmem_cache_node(struct meminfo *si, ulong slab_page)
if (vt->flags & CONFIG_NUMA) {
node = page_to_nid(slab_page);
- node_ptr = ULONG(si->cache_buf +
- OFFSET(kmem_cache_node) +
- (sizeof(void *)*node));
+ node_ptr = get_kmem_cache_node_ptr(si, node);
} else
node_ptr = si->cache + OFFSET(kmem_cache_local_node);
diff --git a/symbols.c b/symbols.c
index a7ccdb1..652fce4 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10922,6 +10922,10 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(kmem_cache_red_left_pad));
fprintf(fp, " kmem_cache_node: %ld\n",
OFFSET(kmem_cache_node));
+ fprintf(fp, " kmem_cache_per_node: %ld\n",
+ OFFSET(kmem_cache_per_node));
+ fprintf(fp, " kmem_cache_per_node_node: %ld\n",
+ OFFSET(kmem_cache_per_node_node));
fprintf(fp, " kmem_cache_cpu_slab: %ld\n",
OFFSET(kmem_cache_cpu_slab));
fprintf(fp, " kmem_cache_cpu_partial: %ld\n",
@@ -12042,6 +12046,8 @@ dump_offset_table(char *spec, ulong makestruct)
SIZE(kmem_bufctl_t));
fprintf(fp, " kmem_cache: %ld\n",
SIZE(kmem_cache));
fprintf(fp, " kmem_cache_node: %ld\n",
SIZE(kmem_cache_node));
+ fprintf(fp, " kmem_cache_per_node_ptrs: %ld\n",
+ SIZE(kmem_cache_per_node_ptrs));
fprintf(fp, " kmem_cache_cpu: %ld\n",
SIZE(kmem_cache_cpu));
fprintf(fp, " swap_info_struct: %ld\n",
--
2.52.0
--
Crash-utility mailing list -- devel(a)lists.crash-utility.osci.io
To unsubscribe send an email to devel-leave(a)lists.crash-utility.osci.io
https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/
Contribution Guidelines:
https://github.com/crash-utility/crash/wiki