[PATCH makedumpfile 3/9] Share page information with extension callbacks
Tao Liu
ltao at redhat.com
Tue Aug 11 21:01:50 PDT 2026
Hi Stephen,
On Tue, Jul 14, 2026 at 12:46 PM Stephen Brennan
<stephen.s.brennan at oracle.com> wrote:
>
> In __exclude_unnecessary_pages(), we extract several fields related
> to the page. Some of these, like compound_order and compound_dtor, have
> logic specific to the kernel version.
>
> Extensions can, of course, determine these values for themselves, but
> it's extra work, and duplicates logic that may need to be updated
> frequently with new kernel versions. What's more, if we put all the
> values together in a single structure, helpers like isSlab() and others
> can be implemented in terms of that structure and shared with the
> extensions in order to further simplify their implementation.
>
> With that in mind, group the per-page variables into a structure and
> share them with extension callbacks. This breaks the extension API,
> but since a release hasn't yet happened, it seems reasonable to do so.
>
> Signed-off-by: Stephen Brennan <stephen.s.brennan at oracle.com>
> ---
> extension.c | 8 ++---
> extension.h | 3 +-
> makedumpfile.c | 83 +++++++++++++++++++++++++-------------------------
> makedumpfile.h | 16 ++++++++++
> 4 files changed, 64 insertions(+), 46 deletions(-)
>
> diff --git a/extension.c b/extension.c
> index 5188c1f..9b29f0c 100644
> --- a/extension.c
> +++ b/extension.c
> @@ -10,7 +10,7 @@
> #include "kallsyms.h"
> #include "btf_info.h"
>
> -typedef int (*callback_fn)(unsigned long, const void *);
> +typedef int (*callback_fn)(unsigned long, const void *, const struct pginfo *);
>
The function signature of callbacks are changed, it will be better to
update them in extensions/sample.c as well:
int extension_callback(unsigned long pfn, const void *pcache)
{
return PG_UNDECID;
}
Since it will serve as a reference for future extension authors.
Thaks,
Tao Liu
> struct extension_handle_cb {
> void *handle;
> @@ -306,14 +306,14 @@ fail:
> * 1) Include the page if anyone says PG_INCLUDE, and
> * 2) Exclude the page if no one says PG_INCLUDE, but one or more say PG_EXCLUDE.
> */
> -int run_extension_callback(unsigned long pfn, const void *pcache)
> +int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *inf)
> {
> int result;
> int ret = PG_UNDECID;
>
> for (int i = 0; i < handle_cbs_len; i++) {
> if (handle_cbs[i]->cb) {
> - result = handle_cbs[i]->cb(pfn, pcache);
> + result = handle_cbs[i]->cb(pfn, pcache, inf);
> if (result == PG_INCLUDE) {
> ret = result;
> goto out;
> @@ -341,7 +341,7 @@ bool add_extension_opts(char *opt)
> return false;
> }
>
> -int run_extension_callback(unsigned long pfn, const void *pcache)
> +int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i)
> {
> return PG_UNDECID;
> }
> diff --git a/extension.h b/extension.h
> index ba8d32a..22af9a6 100644
> --- a/extension.h
> +++ b/extension.h
> @@ -2,12 +2,13 @@
> #define _EXTENSION_H
> #include <stdbool.h>
>
> +struct pginfo;
> enum {
> PG_INCLUDE, // Exntesion will keep the page
> PG_EXCLUDE, // Exntesion will discard the page
> PG_UNDECID, // Exntesion makes no decision
> };
> -int run_extension_callback(unsigned long pfn, const void *pcache);
> +int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i);
> void init_extensions(void);
> void cleanup_extensions(void);
> bool add_extension_opts(char *opt);
> diff --git a/makedumpfile.c b/makedumpfile.c
> index e882b84..a4c9bbf 100644
> --- a/makedumpfile.c
> +++ b/makedumpfile.c
> @@ -6466,12 +6466,13 @@ __exclude_unnecessary_pages(unsigned long mem_map,
> mdf_pfn_t pfn_read_start, pfn_read_end;
> unsigned char *page_cache;
> unsigned char *pcache;
> - unsigned int _count, _mapcount = 0, compound_order = 0;
> + struct pginfo i;
> unsigned int order_offset, dtor_offset;
> - unsigned long flags, mapping, private = 0;
> - unsigned long compound_dtor, compound_head = 0;
> int filter_pg;
>
> + i._mapcount = i.compound_order = 0;
> + i.private = i.compound_dtor = i.compound_head = 0;
> +
> /*
> * If a multi-page exclusion is pending, do it first
> */
> @@ -6543,21 +6544,21 @@ __exclude_unnecessary_pages(unsigned long mem_map,
> pfn_read_end = pfn + pfn_mm - 1;
> }
>
> - flags = ULONG(pcache + OFFSET(page.flags));
> - _count = UINT(pcache + OFFSET(page._refcount));
> - mapping = ULONG(pcache + OFFSET(page.mapping));
> + i.flags = ULONG(pcache + OFFSET(page.flags));
> + i._count = UINT(pcache + OFFSET(page._refcount));
> + i.mapping = ULONG(pcache + OFFSET(page.mapping));
>
> if (OFFSET(page._mapcount) != NOT_FOUND_STRUCTURE)
> - _mapcount = UINT(pcache + OFFSET(page._mapcount));
> + i._mapcount = UINT(pcache + OFFSET(page._mapcount));
>
> - compound_order = 0;
> - compound_dtor = 0;
> + i.compound_order = 0;
> + i.compound_dtor = 0;
> /*
> * The last pfn of the mem_map cache must not be compound head
> * page since all compound pages are aligned to its page order
> * and PGMM_CACHED is a power of 2.
> */
> - if ((index_pg < PGMM_CACHED - 1) && isCompoundHead(flags)) {
> + if ((index_pg < PGMM_CACHED - 1) && isCompoundHead(i.flags)) {
> unsigned char *addr = pcache + SIZE(page);
>
> /*
> @@ -6567,10 +6568,10 @@ __exclude_unnecessary_pages(unsigned long mem_map,
> if (NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE) != NOT_FOUND_NUMBER) {
> unsigned long _flags_1 = ULONG(addr + OFFSET(page.flags));
>
> - compound_order = _flags_1 & 0xff;
> + i.compound_order = _flags_1 & 0xff;
>
> - if (_mapcount == (int)NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE))
> - compound_dtor = IS_HUGETLB;
> + if (i._mapcount == (int)NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE))
> + i.compound_dtor = IS_HUGETLB;
>
> goto check_order;
> }
> @@ -6582,19 +6583,19 @@ __exclude_unnecessary_pages(unsigned long mem_map,
> if (NUMBER(PG_hugetlb) != NOT_FOUND_NUMBER) {
> unsigned long _flags_1 = ULONG(addr + OFFSET(page.flags));
>
> - compound_order = _flags_1 & 0xff;
> + i.compound_order = _flags_1 & 0xff;
>
> if (_flags_1 & (1UL << NUMBER(PG_hugetlb)))
> - compound_dtor = IS_HUGETLB;
> + i.compound_dtor = IS_HUGETLB;
>
> goto check_order;
> }
>
> if (order_offset) {
> if (info->kernel_version >= KERNEL_VERSION(4, 16, 0))
> - compound_order = UCHAR(addr + order_offset);
> + i.compound_order = UCHAR(addr + order_offset);
> else
> - compound_order = USHORT(addr + order_offset);
> + i.compound_order = USHORT(addr + order_offset);
> }
>
> if (dtor_offset) {
> @@ -6603,40 +6604,40 @@ __exclude_unnecessary_pages(unsigned long mem_map,
> * to the ID of it since linux-4.4.
> */
> if (info->kernel_version >= KERNEL_VERSION(4, 16, 0))
> - compound_dtor = UCHAR(addr + dtor_offset);
> + i.compound_dtor = UCHAR(addr + dtor_offset);
> else if (info->kernel_version >= KERNEL_VERSION(4, 4, 0))
> - compound_dtor = USHORT(addr + dtor_offset);
> + i.compound_dtor = USHORT(addr + dtor_offset);
> else
> - compound_dtor = ULONG(addr + dtor_offset);
> + i.compound_dtor = ULONG(addr + dtor_offset);
> }
> check_order:
> - if ((compound_order >= sizeof(unsigned long) * 8)
> - || ((pfn & ((1UL << compound_order) - 1)) != 0)) {
> + if ((i.compound_order >= sizeof(unsigned long) * 8)
> + || ((pfn & ((1UL << i.compound_order) - 1)) != 0)) {
> /* Invalid order */
> - compound_order = 0;
> + i.compound_order = 0;
> }
> }
> if (OFFSET(page.compound_head) != NOT_FOUND_STRUCTURE)
> - compound_head = ULONG(pcache + OFFSET(page.compound_head));
> + i.compound_head = ULONG(pcache + OFFSET(page.compound_head));
>
> if (OFFSET(page.private) != NOT_FOUND_STRUCTURE)
> - private = ULONG(pcache + OFFSET(page.private));
> + i.private = ULONG(pcache + OFFSET(page.private));
>
> - nr_pages = 1 << compound_order;
> + nr_pages = 1 << i.compound_order;
> pfn_counter = NULL;
>
> /*
> * Excludable compound tail pages must have already been excluded by
> * exclude_range(), don't need to check them here.
> */
> - if (compound_head & 1)
> + if (i.compound_head & 1)
> continue;
>
> /*
> * Include pages that specified by user via
> * makedumpfile extensions
> */
> - filter_pg = run_extension_callback(pfn, pcache);
> + filter_pg = run_extension_callback(pfn, pcache, &i);
> if (filter_pg == PG_INCLUDE)
> continue;
>
> @@ -6646,14 +6647,14 @@ check_order:
> */
> if ((info->dump_level & DL_EXCLUDE_FREE)
> && info->page_is_buddy
> - && info->page_is_buddy(flags, _mapcount, private, _count)) {
> + && info->page_is_buddy(i.flags, i._mapcount, i.private, i._count)) {
> if ((ARRAY_LENGTH(zone.free_area) != NOT_FOUND_STRUCTURE) &&
> - (private >= ARRAY_LENGTH(zone.free_area))) {
> + (i.private >= ARRAY_LENGTH(zone.free_area))) {
> MSG("WARNING: Invalid free page order: pfn=%llx, order=%lu, max order=%lu\n",
> - pfn, private, ARRAY_LENGTH(zone.free_area) - 1);
> + pfn, i.private, ARRAY_LENGTH(zone.free_area) - 1);
> continue;
> }
> - nr_pages = 1 << private;
> + nr_pages = 1 << i.private;
> pfn_counter = &pfn_free;
> }
> /*
> @@ -6663,7 +6664,7 @@ check_order:
> * accepted immediately without being on the list.
> */
> else if ((info->dump_level & DL_EXCLUDE_FREE)
> - && isUnaccepted(_mapcount)) {
> + && isUnaccepted(i._mapcount)) {
> nr_pages = 1 << (ARRAY_LENGTH(zone.free_area) - 1);
> pfn_counter = &pfn_free;
> }
> @@ -6671,17 +6672,17 @@ check_order:
> * Exclude the non-private cache page.
> */
> else if ((info->dump_level & DL_EXCLUDE_CACHE)
> - && is_cache_page(flags)
> - && !isPrivate(flags) && !isAnon(mapping, flags, _mapcount)) {
> + && is_cache_page(i.flags)
> + && !isPrivate(i.flags) && !isAnon(i.mapping, i.flags, i._mapcount)) {
> pfn_counter = &pfn_cache;
> }
> /*
> * Exclude the cache page whether private or non-private.
> */
> else if ((info->dump_level & DL_EXCLUDE_CACHE_PRI)
> - && is_cache_page(flags)
> - && !isAnon(mapping, flags, _mapcount)) {
> - if (isPrivate(flags))
> + && is_cache_page(i.flags)
> + && !isAnon(i.mapping, i.flags, i._mapcount)) {
> + if (isPrivate(i.flags))
> pfn_counter = &pfn_cache_private;
> else
> pfn_counter = &pfn_cache;
> @@ -6692,19 +6693,19 @@ check_order:
> * - hugetlbfs pages
> */
> else if ((info->dump_level & DL_EXCLUDE_USER_DATA)
> - && (isAnon(mapping, flags, _mapcount) || isHugetlb(compound_dtor))) {
> + && (isAnon(i.mapping, i.flags, i._mapcount) || isHugetlb(i.compound_dtor))) {
> pfn_counter = &pfn_user;
> }
> /*
> * Exclude the hwpoison page.
> */
> - else if (isHWPOISON(flags)) {
> + else if (isHWPOISON(i.flags)) {
> pfn_counter = &pfn_hwpoison;
> }
> /*
> * Exclude pages that are logically offline.
> */
> - else if (isOffline(flags, _mapcount)) {
> + else if (isOffline(i.flags, i._mapcount)) {
> pfn_counter = &pfn_offline;
> }
> /*
> diff --git a/makedumpfile.h b/makedumpfile.h
> index 4f707c7..87f973d 100644
> --- a/makedumpfile.h
> +++ b/makedumpfile.h
> @@ -1507,6 +1507,22 @@ struct ppc64_vmemmap {
> unsigned long virt;
> };
>
> +/* Per-page information determined during page filtering which may be useful
> + * to extensions making their decisions */
> +struct pginfo {
> + unsigned long flags;
> + unsigned long mapping;
> + /* Present whenever OFFSET(page.private) != NOT_FOUND_STRUCTURE */
> + unsigned long private;
> + unsigned long compound_dtor;
> + /* Present whenever OFFSET(page.compound_head) != NOT_FOUND_STRUCTURE */
> + unsigned long compound_head;
> + unsigned int _count;
> + /* Present whenever OFFSET(page._mapcount) != NOT_FOUND_STRUCTURE */
> + unsigned int _mapcount;
> + unsigned int compound_order;
> +};
> +
> struct DumpInfo {
> int32_t kernel_version; /* version of first kernel*/
> struct timeval timestamp;
> --
> 2.47.3
>
More information about the kexec
mailing list