* [PATCH 1/6] mm/page_owner: extract skip_buddy_pages() helper to unify buddy page skipping
[not found] <20260623065234.31866-1-ye.liu@linux.dev>
@ 2026-06-23 6:52 ` Ye Liu
2026-06-23 6:52 ` [PATCH 2/6] mm/page_owner: use MIGRATE_REASON_NONE instead of -1 for last_migrate_reason Ye Liu
` (4 subsequent siblings)
5 siblings, 0 replies; 6+ messages in thread
From: Ye Liu @ 2026-06-23 6:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka
Cc: Ye Liu, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-kernel
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.
Signed-off-by: Ye Liu <ye.liu@linux.dev>
---
mm/page_owner.c | 52 ++++++++++++++++++++++++-------------------------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 2dddcb6510aa..342549891a8d 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -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;
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 2/6] mm/page_owner: use MIGRATE_REASON_NONE instead of -1 for last_migrate_reason
[not found] <20260623065234.31866-1-ye.liu@linux.dev>
2026-06-23 6:52 ` [PATCH 1/6] mm/page_owner: extract skip_buddy_pages() helper to unify buddy page skipping Ye Liu
@ 2026-06-23 6:52 ` Ye Liu
2026-06-23 6:52 ` [PATCH 3/6] mm/page_owner: hoist CONFIG_MEMCG to function level for print_page_owner_memcg() Ye Liu
` (3 subsequent siblings)
5 siblings, 0 replies; 6+ messages in thread
From: Ye Liu @ 2026-06-23 6:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka
Cc: Ye Liu, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-kernel
The 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.
Signed-off-by: Ye Liu <ye.liu@linux.dev>
---
mm/page_owner.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 342549891a8d..ebafa9d7ff07 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -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:
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/6] mm/page_owner: hoist CONFIG_MEMCG to function level for print_page_owner_memcg()
[not found] <20260623065234.31866-1-ye.liu@linux.dev>
2026-06-23 6:52 ` [PATCH 1/6] mm/page_owner: extract skip_buddy_pages() helper to unify buddy page skipping Ye Liu
2026-06-23 6:52 ` [PATCH 2/6] mm/page_owner: use MIGRATE_REASON_NONE instead of -1 for last_migrate_reason Ye Liu
@ 2026-06-23 6:52 ` Ye Liu
2026-06-23 6:52 ` [PATCH 4/6] mm/page_owner: add missing newline to count_threshold format string Ye Liu
` (2 subsequent siblings)
5 siblings, 0 replies; 6+ messages in thread
From: Ye Liu @ 2026-06-23 6:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka
Cc: Ye Liu, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-kernel
The 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.
Signed-off-by: Ye Liu <ye.liu@linux.dev>
---
mm/page_owner.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index ebafa9d7ff07..5d8773c90cd9 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -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,
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 4/6] mm/page_owner: add missing newline to count_threshold format string
[not found] <20260623065234.31866-1-ye.liu@linux.dev>
` (2 preceding siblings ...)
2026-06-23 6:52 ` [PATCH 3/6] mm/page_owner: hoist CONFIG_MEMCG to function level for print_page_owner_memcg() Ye Liu
@ 2026-06-23 6:52 ` Ye Liu
2026-06-23 6:52 ` [PATCH 5/6] mm/page_owner: move free_ts_nsec output to free section in __dump_page_owner() Ye Liu
2026-06-23 6:52 ` [PATCH 6/6] mm/page_owner: drop redundant page_owner prefix from static symbols Ye Liu
5 siblings, 0 replies; 6+ messages in thread
From: Ye Liu @ 2026-06-23 6:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka
Cc: Ye Liu, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-kernel
The 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.
Signed-off-by: Ye Liu <ye.liu@linux.dev>
---
mm/page_owner.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 5d8773c90cd9..958151ccb587 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -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)
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 5/6] mm/page_owner: move free_ts_nsec output to free section in __dump_page_owner()
[not found] <20260623065234.31866-1-ye.liu@linux.dev>
` (3 preceding siblings ...)
2026-06-23 6:52 ` [PATCH 4/6] mm/page_owner: add missing newline to count_threshold format string Ye Liu
@ 2026-06-23 6:52 ` Ye Liu
2026-06-23 6:52 ` [PATCH 6/6] mm/page_owner: drop redundant page_owner prefix from static symbols Ye Liu
5 siblings, 0 replies; 6+ messages in thread
From: Ye Liu @ 2026-06-23 6:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka
Cc: Ye Liu, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-kernel
The 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.
Signed-off-by: Ye Liu <ye.liu@linux.dev>
---
mm/page_owner.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 958151ccb587..eaccff355cb4 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -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);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 6/6] mm/page_owner: drop redundant page_owner prefix from static symbols
[not found] <20260623065234.31866-1-ye.liu@linux.dev>
` (4 preceding siblings ...)
2026-06-23 6:52 ` [PATCH 5/6] mm/page_owner: move free_ts_nsec output to free section in __dump_page_owner() Ye Liu
@ 2026-06-23 6:52 ` Ye Liu
5 siblings, 0 replies; 6+ messages in thread
From: Ye Liu @ 2026-06-23 6:52 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka
Cc: Ye Liu, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-kernel
All 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.
Signed-off-by: Ye Liu <ye.liu@linux.dev>
---
mm/page_owner.c | 35 +++++++++++++++++------------------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index eaccff355cb4..ec9600025127 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -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)
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread