On Thu, May 14, 2026 at 6:33 PM Huang Shijie <huangsj(a)hygon.cn> wrote:
The folio_order() has three versions:
1.) In verion 1, folio_order() uses page[1].compound_order to
get the folio order. We use "folio_batch" as the indicator.
Please refer to patch set:
https://lore.kernel.org/all/20211208042256.1923824-1-willy@infradead.org/
2.) In version 2, the following patch introduces _folio_order:
c3a15bff46cb5149 "mm: reimplement folio_order() and folio_nr_pages()"
We use "_folio_order" as the indicator.
Please refer to patch set:
https://lore.kernel.org/all/20220808193430.3378317-1-willy@infradead.org/
3.) In version 3, the following patch replaces the _folio_order with _flags_1:
ebc1baf5c9b46c22 "mm: free up a word in the first tail page"
folio_order() uses _flags_1 to get the folio order.
We use "free_huge_folio" as the indicator.
Please refer to patch set:
https://lore.kernel.org/linux-mm/20230816151201.3655946-1-willy@infradead...
(Note, we do not use the struct modifications within the patch set as the
indicator,
since they have been removed in later kernel.)
This patch will be used in later patches.
Signed-off-by: Huang Shijie <huangsj(a)hygon.cn>
---
defs.h | 8 ++++++
memory.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
symbols.c | 6 +++++
3 files changed, 94 insertions(+)
diff --git a/defs.h b/defs.h
index 89044b1..bd996ef 100644
--- a/defs.h
+++ b/defs.h
@@ -2291,6 +2291,9 @@ struct offset_table { /* stash of commonly-used
offsets */
long hrtimer_clock_base_index;
long klp_patch_list;
long tk_data_timekeeper;
+ long page_compound_order;
+ long folio__folio_order;
+ long folio__flags_1;
};
struct size_table { /* stash of commonly-used sizes */
@@ -2470,6 +2473,9 @@ struct size_table { /* stash of commonly-used sizes */
long cpumask_t;
long task_struct_exit_state;
long bpf_ringbuf_map;
+ long page_compound_order;
+ long folio__folio_order;
+ long folio__flags_1;
};
struct array_table {
@@ -5998,6 +6004,8 @@ ulong do_xarray(ulong, int, struct list_pair *);
#define XARRAY_TAG_MASK (3UL)
#define XARRAY_TAG_INTERNAL (2UL)
+int folio_order(ulong folio);
+
int file_dump(ulong, ulong, ulong, int, int);
#define DUMP_FULL_NAME 0x1
#define DUMP_INODE_ONLY 0x2
diff --git a/memory.c b/memory.c
index 17423a5..3deae36 100644
--- a/memory.c
+++ b/memory.c
@@ -410,6 +410,10 @@ mem_init(void)
DISPLAY_DEFAULT = (sizeof(long) == 8) ? DISPLAY_64 : DISPLAY_32;
}
+#define FOLIO_ORDER_V1 1
+#define FOLIO_ORDER_V2 2
+#define FOLIO_ORDER_V3 3
+static int folio_order_version;
/*
* Stash a few popular offsets and some basic kernel virtual memory
@@ -547,6 +551,38 @@ vm_init(void)
MEMBER_OFFSET_INIT(page_freelist, "page", "freelist");
MEMBER_OFFSET_INIT(page_page_type, "page", "page_type");
+ /*
+ * The "folio_batch" was introduced at patch set:
+ *
https://lore.kernel.org/all/20211208042256.1923824-1-willy@infradead.org/
+ * Use it as the indicator for folio_order() version 1.
+ */
+ if (STRUCT_EXISTS("folio_batch"))
+ folio_order_version = FOLIO_ORDER_V1;
+
+ MEMBER_OFFSET_INIT(page_compound_order, "page",
"compound_order");
+ MEMBER_SIZE_INIT(page_compound_order, "page",
"compound_order");
+
+ /*
+ * The "_folio_order" was introduced at patch set:
+ *
https://lore.kernel.org/all/20220808193430.3378317-1-willy@infradead.org/
+ * Use it as the indicator for folio_order() version 2.
+ */
+ MEMBER_SIZE_INIT(folio__folio_order, "folio",
"_folio_order");
+ MEMBER_OFFSET_INIT(folio__folio_order, "folio",
"_folio_order");
+ if (VALID_MEMBER(folio__folio_order))
+ folio_order_version = FOLIO_ORDER_V2;
+
+ /*
+ * The "free_huge_folio()" was introduced at patch set:
+ *
https://lore.kernel.org/linux-mm/20230816151201.3655946-1-willy@infradead...
+ * Use it as the indicator for folio_order() version 3.
+ */
+ MEMBER_OFFSET_INIT(folio__flags_1, "folio", "_flags_1");
+ MEMBER_SIZE_INIT(folio__flags_1, "folio", "_flags_1");
+ if (kernel_symbol_exists("free_huge_folio") ||
+ THIS_KERNEL_VERSION >= LINUX(6,6,0))
I think the "free_huge_folio" check is enough, THIS_KERNEL_VERSION >=
LINUX(6,6,0) is not needed. Again, we should avoid the macro as much
as possible.
+ folio_order_version = FOLIO_ORDER_V3;
+
MEMBER_OFFSET_INIT(mm_struct_pgd, "mm_struct", "pgd");
MEMBER_OFFSET_INIT(swap_info_struct_swap_file,
@@ -20426,6 +20462,50 @@ static unsigned int oo_objects(ulong oo)
return (oo & ((1 << 16) - 1));
}
+int
+folio_order(ulong folio)
+{
+ ulong v = 0;
+ int PG_head = 16;
+
+ if (folio_order_version == FOLIO_ORDER_V1) {
+ readmem(folio + OFFSET(page_flags), KVADDR, &v, sizeof(ulong),
+ "folio.page.flags", FAULT_ON_ERROR);
+ if (!(v & (1 << PG_head)))
+ return 0;
+
+ readmem(folio + SIZE(page) + OFFSET(page_compound_order), KVADDR,
&v,
+ SIZE(page_compound_order), "page[1].compound_order",
FAULT_ON_ERROR);
+
+ return v;
+ } else if (folio_order_version == FOLIO_ORDER_V2) {
+ readmem(folio + OFFSET(page_flags), KVADDR, &v, sizeof(ulong),
+ "folio.page.flags", FAULT_ON_ERROR);
+ if (!(v & (1 << PG_head)))
+ return 0;
+
+ readmem(folio + OFFSET(folio__folio_order), KVADDR, &v,
+ SIZE(folio__folio_order), "folio->_folio_order",
FAULT_ON_ERROR);
+
+ return v;
+ } else if (folio_order_version == FOLIO_ORDER_V3) {
+ /* The PG_head changes to bit 6 in this version */
+ PG_head = 6;
+
+ readmem(folio + OFFSET(page_flags), KVADDR, &v, sizeof(ulong),
+ "folio.page.flags", FAULT_ON_ERROR);
+ if (!(v & (1 << PG_head)))
+ return 0;
+
+ readmem(folio + OFFSET(folio__flags_1), KVADDR, &v,
+ SIZE(folio__flags_1), "folio->_flags_1",
FAULT_ON_ERROR);
+
+ return v & 0xff;
+ } else {
+ return 0;
+ }
+}
+
#ifdef NOT_USED
ulong
slab_to_kmem_cache_node(struct meminfo *si, ulong slab_page)
diff --git a/symbols.c b/symbols.c
index 3c62f54..c028505 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10462,6 +10462,9 @@ dump_offset_table(char *spec, ulong makestruct)
fprintf(fp, " page_private: %ld\n",
OFFSET(page_private));
fprintf(fp, " page_page_type: %ld\n",
OFFSET(page_page_type));
+ fprintf(fp, " page_compound_order: %ld\n",
OFFSET(page_compound_order));
+ fprintf(fp, " folio__folio_order: %ld\n",
OFFSET(folio__folio_order));
+ fprintf(fp, " folio__flags_1: %ld\n",
OFFSET(folio__flags_1));
fprintf(fp, " trace_print_flags_mask: %ld\n",
OFFSET(trace_print_flags_mask));
@@ -11972,6 +11975,9 @@ dump_offset_table(char *spec, ulong makestruct)
fprintf(fp, "\n size_table:\n");
fprintf(fp, " page: %ld\n", SIZE(page));
fprintf(fp, " page_flags: %ld\n",
SIZE(page_flags));
+ fprintf(fp, " page_compound_order: %ld\n",
SIZE(page_compound_order));
+ fprintf(fp, " folio__folio_order: %ld\n",
SIZE(folio__folio_order));
+ fprintf(fp, " folio__flags_1: %ld\n",
SIZE(folio__flags_1));
fprintf(fp, " trace_print_flags: %ld\n",
SIZE(trace_print_flags));
fprintf(fp, " free_area_struct: %ld\n",
SIZE(free_area_struct));
--
2.53.0