mm/page_owner: extract skip_buddy_pages() helper to unify buddy page skipping
Subsystems (2)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/6 mm/page_owner: extract skip_buddy_pages() helper to unify buddy page skipping
Three places in page_owner.c duplicate the same pattern: check if a page is PageBuddy, read its order via buddy_order_unsafe(), advance the pfn past the buddy block if the order is valid, and continue.
Consolidate them into a single inline helper skip_buddy_pages(). The function returns true (skip) for any buddy page and advances @pfn past the block when the order is valid; returns false if the page is not a buddy page and should be processed normally.
The old init_pages_in_zone() variant used "order > 0" as an extra guard before advancing pfn, but the continue was unconditional and (1UL << 0) - 1 == 0, so the behaviour is identical. The comment about zone->lock is preserved in the helper's kernel-doc.
No functional change.
--- mm/page_owner.c | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-)
mm/page_owner.c +26 -26
@@ -422,6 +422,29 @@ void __folio_copy_owner(struct folio *newfolio, struct folio *old)rcu_read_unlock (); } +/* +* Check if a page is a buddy page and advance @pfn past the entire buddy block. +* This safely reads the buddy order without the zone lock, which may cause us +* to skip less than the full buddy block, but that is acceptable for page owner +* iteration purposes. +* +* Return: true if the page was skipped (caller should continue its loop), +* false if the page is not a buddy page and should be processed normally. +*/ +static inline bool skip_buddy_pages (unsigned long *pfn,struct page *page) +{ +unsigned long order; + +if (!PageBuddy (page)) +return false; + + order =buddy_order_unsafe (page); +if (order <=MAX_PAGE_ORDER ) + *pfn += (1UL << order) -1 ; + +return true; +} +void pagetypeinfo_showmixedcount_print (struct seq_file *m,pg_data_t *pgdat,struct zone *zone) {
@@ -461,14 +484,8 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,if (page_zone (page) != zone)continue ; -if (PageBuddy (page)) { -unsigned long freepage_order; - - freepage_order =buddy_order_unsafe (page); -if (freepage_order <=MAX_PAGE_ORDER ) - pfn += (1UL << freepage_order) -1 ; +if (skip_buddy_pages (&pfn, page))continue ; - }if (PageReserved (page))continue ;
@@ -697,13 +714,8 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos) } page =pfn_to_page (pfn); -if (PageBuddy (page)) { -unsigned long freepage_order =buddy_order_unsafe (page); - -if (freepage_order <=MAX_PAGE_ORDER ) - pfn += (1UL << freepage_order) -1 ; +if (skip_buddy_pages (&pfn, page))continue ; - } page_ext =page_ext_get (page);if (unlikely (!page_ext))
@@ -798,20 +810,8 @@ static void init_pages_in_zone(struct zone *zone)if (page_zone (page) != zone)continue ; -/* -* To avoid having to grab zone->lock, be a little -* careful when reading buddy page order. The only -* danger is that we skip too much and potentially miss -* some early allocated pages, which is better than -* heavy lock contention. -*/ -if (PageBuddy (page)) { -unsigned long order =buddy_order_unsafe (page); - -if (order >0 && order <=MAX_PAGE_ORDER ) - pfn += (1UL << order) -1 ; +if (skip_buddy_pages (&pfn, page))continue ; - }if (PageReserved (page))continue ;
Signature · 1 line
2.43.0
Andrew Morton
maintainerQuoted 9 lines · On Tue, 23 Jun 2026 14:52:26 +0800 Ye Liu <[email protected]> wrote:
Three places in
page_owner.cduplicate the same pattern: check if a page is PageBuddy, read its order viabuddy_order_unsafe(), advance the pfn past the buddy block if the order is valid, and continue.Consolidate them into a single inline helper
skip_buddy_pages(). The function returns true (skip) for any buddy page and advances @pfn past the block when the order is valid; returns false if the page is not a buddy page and should be processed normally.
Quoted 4 lines
The old
init_pages_in_zone()variant used "order > 0" as an extra guard before advancing pfn, but the continue was unconditional and (1UL << 0) - 1 == 0, so the behaviour is identical. The comment aboutzone->lockis preserved in the helper's kernel-doc.
All looks nice, thanks.
A [0/N] cover letter is nice to have.
AI review identified a few possible pre-existing issues, if you're interested:
https://sashiko.dev/#/patchset/[email protected]
Vlastimil Babka (SUSE)
maintainerQuoted 11 lines · Andrew Morton
Quoted 9 lines · On Tue, 23 Jun 2026 14:52:26 +0800 Ye Liu <[email protected]> wrote:
Three places in
page_owner.cduplicate the same pattern: check if a page is PageBuddy, read its order viabuddy_order_unsafe(), advance the pfn past the buddy block if the order is valid, and continue.Consolidate them into a single inline helper
skip_buddy_pages(). The function returns true (skip) for any buddy page and advances @pfn past the block when the order is valid; returns false if the page is not a buddy page and should be processed normally.
Quoted 6 lines
Quoted 4 lines
The old
init_pages_in_zone()variant used "order > 0" as an extra guard before advancing pfn, but the continue was unconditional and (1UL << 0) - 1 == 0, so the behaviour is identical. The comment aboutzone->lockis preserved in the helper's kernel-doc.All looks nice, thanks.
I got a bunch of "added to mm-hotfixes-unstable branch" mails, but this seems like cleanups and nothing urgent? Was that intended?
Quoted 1 line
A [0/N] cover letter is nice to have.
Seems like it exists, but wasn't delivered. Lore shows its message id, but as missing.
Quoted 4 lines
AI review identified a few possible pre-existing issues, if you're interested:
https://sashiko.dev/#/patchset/[email protected]
Quoted 19 lines · Vlastimil Babka (SUSE)
Quoted 11 lines · Andrew Morton
Quoted 9 lines · On Tue, 23 Jun 2026 14:52:26 +0800 Ye Liu <[email protected]> wrote:
Three places in
page_owner.cduplicate the same pattern: check if a page is PageBuddy, read its order viabuddy_order_unsafe(), advance the pfn past the buddy block if the order is valid, and continue.Consolidate them into a single inline helper
skip_buddy_pages(). The function returns true (skip) for any buddy page and advances @pfn past the block when the order is valid; returns false if the page is not a buddy page and should be processed normally.Quoted 6 lines
Quoted 4 lines
The old
init_pages_in_zone()variant used "order > 0" as an extra guard before advancing pfn, but the continue was unconditional and (1UL << 0) - 1 == 0, so the behaviour is identical. The comment aboutzone->lockis preserved in the helper's kernel-doc.All looks nice, thanks.
Quoted 7 lines
I got a bunch of "added to mm-hotfixes-unstable branch" mails, but this seems like cleanups and nothing urgent? Was that intended?
Quoted 1 line
A [0/N] cover letter is nice to have.
Seems like it exists, but wasn't delivered. Lore shows its message id, but as missing.
Apologies, I accidentally sent the cover letter only to my own address. I'll ensure it goes to the mailing list in future submissions. Thanks for pointing it out.
Subject: [PATCH 0/6] mm/page_owner: misc cleanups
Hi,
This series collects a few cleanups for mm/page_owner.c that have been accumulated while reading through the file. There is no functional change -- the goal is to make the code easier to read and maintain.
Patch 1 consolidates three identical PageBuddy skip blocks into a single skip_buddy_pages() helper, eliminating the duplication and keeping the lockless-read comment in one place.
Patch 2 replaces the -1 magic number used for "never migrated" with a local MIGRATE_REASON_NONE define, making the intent explicit at every use site.
Patch 3 hoists the CONFIG_MEMCG guard out of print_page_owner_memcg()'s body so that the real implementation and the empty stub are two clearly separate definitions, the common kernel idiom.
Patch 4 adds a missing \n to the count_threshold debugfs attribute format string so that cat(1) output is properly terminated.
Patch 5 moves free_ts_nsec from the allocation summary line to the free section in __dump_page_owner(), grouping it with free_pid and free_tgid where it logically belongs. This also makes the dump output consistent with print_page_owner().
Patch 6 drops the redundant page_owner_ prefix from file-scoped static symbols (stack_fops, threshold_fops, etc.). Since they cannot collide across translation units, the prefix carries no information.
The series is based on v6.17-rc1 and has been compile-tested with and without CONFIG_MEMCG.
Ye Liu (6):
mm/page_owner: extract skip_buddy_pages() helper to unify buddy page
skipping
mm/page_owner: use MIGRATE_REASON_NONE instead of -1 for
last_migrate_reason
mm/page_owner: hoist CONFIG_MEMCG to function level for
print_page_owner_memcg()
mm/page_owner: add missing newline to count_threshold format string mm/page_owner: move free_ts_nsec output to free section in
__dump_page_owner()
mm/page_owner: drop redundant page_owner prefix from static symbols
mm/page_owner.c | 121 +++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 54 deletions(-)
Quoted 6 lines
Quoted 5 lines
AI review identified a few possible pre-existing issues, if you're interested:
https://sashiko.dev/#/patchset/[email protected]
Signature · 2 lines
Thanks, Ye Liu
Andrew Morton
maintainerQuoted 20 lines · On Thu, 25 Jun 2026 15:31:08 +0800 Ye Liu <[email protected]> wrote:
Quoted 17 lines · Vlastimil Babka (SUSE)
Quoted 16 lines · Andrew Morton
Quoted 13 lines · On Tue, 23 Jun 2026 14:52:26 +0800 Ye Liu <[email protected]> wrote:
Three places in
page_owner.cduplicate the same pattern: check if a page is PageBuddy, read its order viabuddy_order_unsafe(), advance the pfn past the buddy block if the order is valid, and continue.Consolidate them into a single inline helper
skip_buddy_pages(). The function returns true (skip) for any buddy page and advances @pfn past the block when the order is valid; returns false if the page is not a buddy page and should be processed normally.The old
init_pages_in_zone()variant used "order > 0" as an extra guard before advancing pfn, but the continue was unconditional and (1UL << 0) - 1 == 0, so the behaviour is identical. The comment aboutzone->lockis preserved in the helper's kernel-doc.
Quoted 4 lines
Quoted 4 lines
Quoted 1 line
All looks nice, thanks.
I got a bunch of "added to mm-hotfixes-unstable branch" mails, but this seems like cleanups and nothing urgent? Was that intended?
Yeah, sorry, I soon after moved these into mm-new.
Quoted 11 lines
Quoted 4 lines
Quoted 1 line
A [0/N] cover letter is nice to have.
Seems like it exists, but wasn't delivered. Lore shows its message id, but as missing.
Apologies, I accidentally sent the cover letter only to my own address. I'll ensure it goes to the mailing list in future submissions. Thanks for pointing it out.
Subject: [PATCH 0/6]
mm/page_owner: misc cleanups
Please send a full v2 to tidy all this up.
PATCH 2/6 mm/page_owner: use MIGRATE_REASON_NONE instead of -1 for last_migrate_reason
In reply to a message not in the archiveThe last_migrate_reason field uses -1 as a sentinel value to mean "no migration has happened". Replace the four bare -1 occurrences with a local MIGRATE_REASON_NONE define so the intent is explicit and the magic number is eliminated.
No functional change.
--- mm/page_owner.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
mm/page_owner.c +11 -4
@@ -21,6 +21,13 @@*/ #define PAGE_OWNER_STACK_DEPTH (16) +/* +* Used to indicate that a page has never been migrated, as the valid +* migrate_reason values are non-negative enum members (MR_* in +* include/linux/migrate_mode.h). +*/ +#define MIGRATE_REASON_NONE (-1) +struct page_owner {unsigned short order;short last_migrate_reason;
@@ -339,7 +346,7 @@ noinline void __set_page_owner(struct page *page, unsigned short order,depot_stack_handle_t handle; handle =save_stack (gfp_mask); -__update_page_owner_handle (page, handle, order, gfp_mask,-1 , +__update_page_owner_handle (page, handle, order, gfp_mask,MIGRATE_REASON_NONE , ts_nsec, current->pid, current->tgid, current->comm);inc_stack_record_count (handle, gfp_mask,1 << order);
@@ -596,7 +603,7 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,if (ret >= count) goto err; -if (page_owner->last_migrate_reason !=-1 ) { +if (page_owner->last_migrate_reason !=MIGRATE_REASON_NONE ) { ret +=scnprintf (kbuf + ret, count - ret,"Page has been migrated, last migrate reason: %s\n" , migrate_reason_names[page_owner->last_migrate_reason]);
@@ -667,7 +674,7 @@ void __dump_page_owner(const struct page *page)stack_depot_print (handle); } -if (page_owner->last_migrate_reason !=-1 ) +if (page_owner->last_migrate_reason !=MIGRATE_REASON_NONE )pr_alert ("page has been migrated, last migrate reason: %s\n" , migrate_reason_names[page_owner->last_migrate_reason]);page_ext_put (page_ext);
@@ -826,7 +833,7 @@ static void init_pages_in_zone(struct zone *zone)/* Found early allocated page */ __update_page_owner_handle (page, early_handle,0 ,0 , --1 ,local_clock (), current->pid, +MIGRATE_REASON_NONE ,local_clock (), current->pid, current->tgid, current->comm); count++; ext_put_continue:
Signature · 1 line
2.43.0
PATCH 3/6 mm/page_owner: hoist CONFIG_MEMCG to function level for print_page_owner_memcg()
In reply to a message not in the archiveThe print_page_owner_memcg() function has CONFIG_MEMCG guarding its entire body via #ifdef inside the function, which leaves a no-op { return ret; } when the config is disabled. Hoist the #ifdef to the top level so the real implementation and the empty stub are two clearly separated definitions.
No functional change.
--- mm/page_owner.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
mm/page_owner.c +8 -2
@@ -529,13 +529,13 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,seq_putc (m,'\n' ); } +#ifdef CONFIG_MEMCG /* * Looking for memcg information and print it out */ static inline int print_page_owner_memcg (char *kbuf,size_t count,int ret,struct page *page) { -#ifdef CONFIG_MEMCG unsigned long memcg_data;struct mem_cgroup *memcg;bool online;
@@ -563,10 +563,16 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, name); out_unlock:rcu_read_unlock (); -#endif /* CONFIG_MEMCG */ return ret; } +#else +static inline int print_page_owner_memcg (char *kbuf,size_t count,int ret, +struct page *page) +{ +return ret; +} +#endif static ssize_t print_page_owner(char __user *buf, size_t count, unsigned long pfn,
Signature · 1 line
2.43.0
PATCH 4/6 mm/page_owner: add missing newline to count_threshold format string
In reply to a message not in the archiveThe DEFINE_SIMPLE_ATTRIBUTE format string for page_owner_threshold_fops is missing a trailing \n. simple_attr_read() uses scnprintf() with the format string, which does not append a newline, so reading /sys/kernel/debug/page_owner_stacks/count_threshold produces output without a terminating newline. Add the missing \n to match the standard debugfs attribute convention.
--- mm/page_owner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/page_owner.c +1 -1
@@ -981,7 +981,7 @@ static int page_owner_threshold_set(void *data, u64 val) }DEFINE_SIMPLE_ATTRIBUTE (page_owner_threshold_fops, &page_owner_threshold_get, - &page_owner_threshold_set,"%llu" ); + &page_owner_threshold_set,"%llu\n" );static int __init pageowner_init (void )
Signature · 1 line
2.43.0
PATCH 5/6 mm/page_owner: move free_ts_nsec output to free section in __dump_page_owner()
In reply to a message not in the archiveThe free_ts_nsec field is a free-event timestamp, but it was printed in the allocation summary line alongside ts_nsec (allocation time). Move it to the free section where it logically belongs, together with free_pid and free_tgid. This also makes __dump_page_owner() consistent with print_page_owner(), which only prints ts_nsec in the allocation summary.
The output now groups all free-related information (pid, tgid, timestamp, stack trace) in one place.
No functional change except output formatting.
--- mm/page_owner.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
mm/page_owner.c +5 -4
@@ -660,10 +660,10 @@ void __dump_page_owner(const struct page *page)else pr_alert ("page_owner tracks the page as freed\n" ); -pr_alert ("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu, free_ts %llu\n" , +pr_alert ("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu\n" , page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask, page_owner->pid, page_owner->tgid, page_owner->comm, - page_owner->ts_nsec, page_owner->free_ts_nsec); + page_owner->ts_nsec); handle =READ_ONCE (page_owner->handle);if (!handle)
@@ -675,8 +675,9 @@ void __dump_page_owner(const struct page *page)if (!handle) {pr_alert ("page_owner free stack trace missing\n" ); }else { -pr_alert ("page last free pid %d tgid %d stack trace:\n" , - page_owner->free_pid, page_owner->free_tgid); +pr_alert ("page last free pid %d tgid %d ts %llu stack trace:\n" , + page_owner->free_pid, page_owner->free_tgid, + page_owner->free_ts_nsec);stack_depot_print (handle); }
Signature · 1 line
2.43.0
PATCH 6/6 mm/page_owner: drop redundant page_owner prefix from static symbols
In reply to a message not in the archiveAll of these symbols are file-scoped (static) in page_owner.c, so the page_owner_ prefix is pure noise. Rename them to shorter, still-clear names:
page_owner_stack_op -> stack_op page_owner_stack_open -> stack_open page_owner_stack_fops -> stack_fops
page_owner_pages_threshold -> pages_threshold
page_owner_threshold_get -> threshold_get
page_owner_threshold_set -> threshold_set
page_owner_threshold_fops -> threshold_fops
No functional change.
--- mm/page_owner.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-)
mm/page_owner.c +17 -18
@@ -901,7 +901,7 @@ static void *stack_next(struct seq_file *m, void *v, loff_t *ppos)return stack; } -static unsigned long page_owner_pages_threshold; +static unsigned long pages_threshold;static int stack_print (struct seq_file *m,void *v) {
@@ -918,7 +918,7 @@ static int stack_print(struct seq_file *m, void *v) nr_base_pages =refcount_read (&stack_record->count) -1 ;if (ctx->flags &STACK_PRINT_FLAG_PAGES && - (nr_base_pages <1 || nr_base_pages < page_owner_pages_threshold)) + (nr_base_pages <1 || nr_base_pages < pages_threshold))return 0 ;if (ctx->flags &STACK_PRINT_FLAG_STACK ) {
@@ -940,16 +940,16 @@ static void stack_stop(struct seq_file *m, void *v) { } -static const struct seq_operations page_owner_stack_op = { +static const struct seq_operations stack_op = { .start = stack_start, .next = stack_next, .stop = stack_stop, .show = stack_print }; -static int page_owner_stack_open (struct inode *inode,struct file *file) +static int stack_open (struct inode *inode,struct file *file) { -int ret =seq_open_private (file, &page_owner_stack_op, +int ret =seq_open_private (file, &stack_op,sizeof (struct stack_print_ctx ));if (!ret) {
@@ -962,28 +962,27 @@ static int page_owner_stack_open(struct inode *inode, struct file *file)return ret; } -static const struct file_operations page_owner_stack_fops = { - .open = page_owner_stack_open, +static const struct file_operations stack_fops = { + .open = stack_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, }; -static int page_owner_threshold_get (void *data,u64 *val) +static int threshold_get (void *data,u64 *val) { - *val =READ_ONCE (page_owner_pages_threshold); + *val =READ_ONCE (pages_threshold);return 0 ; } -static int page_owner_threshold_set (void *data,u64 val) +static int threshold_set (void *data,u64 val) { -WRITE_ONCE (page_owner_pages_threshold, val); +WRITE_ONCE (pages_threshold, val);return 0 ; } -DEFINE_SIMPLE_ATTRIBUTE (page_owner_threshold_fops, &page_owner_threshold_get, - &page_owner_threshold_set,"%llu\n" ); - +DEFINE_SIMPLE_ATTRIBUTE (threshold_fops, &threshold_get, + &threshold_set,"%llu\n" );static int __init pageowner_init (void ) {
@@ -999,17 +998,17 @@ static int __init pageowner_init(void)debugfs_create_file ("show_stacks" ,0400 , dir, (void *)(STACK_PRINT_FLAG_STACK |STACK_PRINT_FLAG_PAGES ), - &page_owner_stack_fops); + &stack_fops);debugfs_create_file ("show_handles" ,0400 , dir, (void *)(STACK_PRINT_FLAG_HANDLE |STACK_PRINT_FLAG_PAGES ), - &page_owner_stack_fops); + &stack_fops);debugfs_create_file ("show_stacks_handles" ,0400 , dir, (void *)(STACK_PRINT_FLAG_STACK |STACK_PRINT_FLAG_HANDLE ), - &page_owner_stack_fops); + &stack_fops);debugfs_create_file ("count_threshold" ,0600 , dir,NULL , - &page_owner_threshold_fops); + &threshold_fops);return 0 ; }late_initcall (pageowner_init)
Signature · 1 line
2.43.0