On 2026/08/12 11:33, Dave Young wrote:
On 7/23/26 1:09 PM, HAGIO KAZUHITO(萩尾 一仁) wrote:
> 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)))
Hi Kazu, BTW, there is one nitpick found by AI, it would be better to improve the above
macro as below or just use the kernel inline function:
#define IS_POWER_OF_TWO(n) ((n) > 0 && ((n) & ((n) - 1)) == 0)
Either is fine, but I think the former is the current is_power_of_2()
used in compound_info_has_mask(), and I used the same one as kernel.
https://github.com/torvalds/linux/commit/4cc67b048459bebb7a60b693044ec83f...
https://github.com/torvalds/linux/blob/master/include/linux/page-flags.h#...
Feel free to take my Tested-by
Thank you for your help!
Thanks,
Kazu
>
>
>> + 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));
> --
> 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