From: Kazuhito Hagio <k-hagio-ab(a)nec.com>
Kernel commit d50569612c29 ("mm: rename the 'compound_head' field in the
'struct page' to 'compound_info'") and related patches [1] changed
the
field name and its value.
Without the patch, crash prints the following warning during startup,
WARNING: SLUB: cannot determine how compound pages are linked
and "bt -F" option fails with the following error.
crash> bt -F
bt: invalid structure member offset: page_first_page
FILE: memory.c LINE: 20238 FUNCTION: compound_head()
[1]
https://lore.kernel.org/all/20260227194302.274384-1-kas@kernel.org/
Signed-off-by: Kazuhito Hagio <k-hagio-ab(a)nec.com>
---
defs.h | 1 +
memory.c | 44 ++++++++++++++++++++++++++++++++++++--------
symbols.c | 1 +
3 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/defs.h b/defs.h
index e3027e2b9141..88b7bbeb2372 100644
--- a/defs.h
+++ b/defs.h
@@ -2295,6 +2295,7 @@ struct offset_table { /* stash of commonly-used
offsets */
long page_compound_order;
long folio__folio_order;
long folio__flags_1;
+ long page_compound_info;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/memory.c b/memory.c
index 3f3aa274ab5a..7102e951f1d5 100644
--- a/memory.c
+++ b/memory.c
@@ -414,6 +414,7 @@ mem_init(void)
#define FOLIO_ORDER_V2 2
#define FOLIO_ORDER_V3 3
static int folio_order_version;
+static int compound_info_has_mask = FALSE;
/*
* Stash a few popular offsets and some basic kernel virtual memory
@@ -547,6 +548,7 @@ vm_init(void)
MEMBER_OFFSET_INIT(page_compound_head, "page",
"compound_head");
if (INVALID_MEMBER(page_compound_head))
ANON_MEMBER_OFFSET_INIT(page_compound_head, "page",
"compound_head");
+ MEMBER_OFFSET_INIT(page_compound_info, "page", "compound_info");
MEMBER_OFFSET_INIT(page_private, "page", "private");
MEMBER_OFFSET_INIT(page_freelist, "page", "freelist");
MEMBER_OFFSET_INIT(page_page_type, "page", "page_type");
@@ -1352,6 +1354,17 @@ vm_init(void)
} else if (CRASHDEBUG(1))
error(NOTE, "page_hash_table does not exist in this kernel\n");
+ /*
+ * on Linux 7.1 and later, zone.vmemmap_tails is defined only when
+ * CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP is enabled.
+ */
+#define is_power_of_2(n) (n - 1 < (n ^ (n - 1)))
+ if (VALID_MEMBER(page_compound_info) &&
+ is_power_of_2(SIZE(page)) && MEMBER_EXISTS("zone",
"vmemmap_tails"))
+ compound_info_has_mask = TRUE;
+ if (CRASHDEBUG(1))
+ error(NOTE, "compound_info_has_mask = %d\n", compound_info_has_mask);
+
kmem_cache_init();
page_flags_init();
@@ -5642,10 +5655,11 @@ PG_slab_flag_init(void)
}
}
- if (VALID_MEMBER(page_compound_head)) {
+ if (VALID_MEMBER(page_compound_head) || VALID_MEMBER(page_compound_info)) {
if (CRASHDEBUG(2))
fprintf(fp,
- "PG_head_tail_mask: (UNUSED): page.compound_head exists!\n");
+ "PG_head_tail_mask: (UNUSED): page.compound_head or "
+ "page.compound_info exists!\n");
} else if (vt->flags & KMALLOC_SLUB) {
/*
* PG_slab and the following are hardwired for
@@ -9858,7 +9872,8 @@ vaddr_to_kmem_cache(ulong vaddr, char *buf, int verbose)
&page_flags, sizeof(ulong), "page.flags",
FAULT_ON_ERROR);
if (!page_slab(page, page_flags)) {
- if (((vt->flags & KMALLOC_SLUB) || VALID_MEMBER(page_compound_head)) ||
+ if (((vt->flags & KMALLOC_SLUB) || VALID_MEMBER(page_compound_head) ||
+ VALID_MEMBER(page_compound_info)) ||
((vt->flags & KMALLOC_COMMON) &&
VALID_MEMBER(page_slab) && VALID_MEMBER(page_first_page))) {
readmem(compound_head(page)+OFFSET(page_flags), KVADDR,
@@ -9873,7 +9888,8 @@ vaddr_to_kmem_cache(ulong vaddr, char *buf, int verbose)
if ((vt->flags & KMALLOC_SLUB) ||
((vt->flags & KMALLOC_COMMON) && VALID_MEMBER(page_slab) &&
- (VALID_MEMBER(page_compound_head) || VALID_MEMBER(page_first_page)))) {
+ (VALID_MEMBER(page_compound_head) || VALID_MEMBER(page_compound_info) ||
+ VALID_MEMBER(page_first_page)))) {
readmem(compound_head(page)+OFFSET(page_slab),
KVADDR, &cache, sizeof(void *),
"page.slab", FAULT_ON_ERROR);
@@ -9904,7 +9920,8 @@ is_slab_overload_page(ulong vaddr, ulong *page_head, char *buf)
if ((vt->flags & SLAB_OVERLOAD_PAGE) &&
is_page_ptr(vaddr, NULL) && VALID_MEMBER(page_slab) &&
- (VALID_MEMBER(page_compound_head) || VALID_MEMBER(page_first_page))) {
+ (VALID_MEMBER(page_compound_head) || VALID_MEMBER(page_compound_info) ||
+ VALID_MEMBER(page_first_page))) {
readmem(compound_head(vaddr)+OFFSET(page_slab),
KVADDR, &cache, sizeof(void *),
"page.slab", FAULT_ON_ERROR);
@@ -9944,7 +9961,8 @@ vaddr_to_slab(ulong vaddr)
slab = 0;
- if ((vt->flags & KMALLOC_SLUB) || VALID_MEMBER(page_compound_head))
+ if ((vt->flags & KMALLOC_SLUB) || VALID_MEMBER(page_compound_head) ||
+ VALID_MEMBER(page_compound_info))
slab = compound_head(page);
else if (vt->flags & SLAB_OVERLOAD_PAGE)
slab = compound_head(page);
@@ -20222,11 +20240,21 @@ get_kmem_cache_child_list(ulong **cache_buf, ulong root)
static ulong
compound_head(ulong page)
{
- ulong flags, first_page, compound_head;
+ ulong flags, first_page, compound_head, info, mask;
first_page = page;
- if (VALID_MEMBER(page_compound_head)) {
+ if (VALID_MEMBER(page_compound_info)) {
+ if (readmem(page + OFFSET(page_compound_info), KVADDR, &info,
+ sizeof(ulong), "page.compound_info", RETURN_ON_ERROR)) {
+ if (compound_info_has_mask) {
+ mask = (info & 1) - 1;
+ mask |= info;
+ first_page = page & mask;
+ } else if (info & 1)
+ first_page = info - 1;
+ }
+ } else if (VALID_MEMBER(page_compound_head)) {
if (readmem(page+OFFSET(page_compound_head), KVADDR, &compound_head,
sizeof(ulong), "page.compound_head", RETURN_ON_ERROR)) {
if (compound_head & 1)
diff --git a/symbols.c b/symbols.c
index 03511c8cbe8c..2071bdf77819 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10504,6 +10504,7 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(page_active));
fprintf(fp, " page_compound_head: %ld\n",
OFFSET(page_compound_head));
+ fprintf(fp, " page_compound_info: %ld\n",
OFFSET(page_compound_info));
fprintf(fp, " page_private: %ld\n",
OFFSET(page_private));
fprintf(fp, " page_page_type: %ld\n",
OFFSET(page_page_type));
--
2.31.1