Skip to contentSkip to conversation

mm/page_owner: misc cleanups

PATCH Superseded Ye Liu 4 participants 13 messages Jun 30

Subsystems (6)

v3 of 6 Superseded Latest is v6 →

lore
Share & tools
lore

Ye Liu

author
#0 lore raw

v3 cover mm/page_owner: misc cleanups

v3:
- Patch 2: add MR_NEVER directly to enum migrate_reason instead of using a local MIGRATE_REASON_NONE define (Zi Yan and Vlastimil Babka).
- Patch 3 (new): convert all 'int reason' parameters in the migration callchain to 'enum migrate_reason' so the type system reflects the actual semantics.
- Patch 7: make threshold_fops a single line.
- Link: https://lore.kernel.org/all/[email protected]/

v2:
- Add cover letter (no code changes).
- Link: https://lore.kernel.org/all/[email protected]/

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 proper MR_NEVER member in enum migrate_reason, and adds the corresponding "never_migrated" string in the MIGRATE_REASON trace macro.

Patch 3 follows up by converting the remaining 'int reason' parameters throughout the migration and hugetlb callchains to 'enum migrate_reason', making the type explicit and gaining compiler checking. The 'short last_migrate_reason' struct field in page_owner is intentionally left as 'short' since it is per-page metadata where size matters.

Patch 4 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 5 adds a missing \n to the count_threshold debugfs attribute format string so that cat(1) output is properly terminated.

Patch 6 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 7 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 (7):
mm/page_owner: extract skip_buddy_pages() helper to unify buddy page
skipping
mm/page_owner: add MR_NEVER to enum migrate_reason and use it for
last_migrate_reason
mm: use enum migrate_reason instead of int for migration reason
parameters
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

 include/linux/hugetlb.h        |   9 +--
 include/linux/migrate.h        |   6 +-
 include/linux/migrate_mode.h   |   1 +
 include/linux/page_owner.h     |   7 +-
 include/trace/events/migrate.h |  11 ++--
 mm/hugetlb.c                   |   3 +-
 mm/migrate.c                   |  12 ++--
 mm/page_owner.c                | 115 +++++++++++++++++----------------
 8 files changed, 88 insertions(+), 76 deletions(-)

--
2.43.0

author
#1 lore raw

PATCH v3 1/7 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
index 2dddcb6510aa..342549891a8d 100644
@@ -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
author
#2 lore raw

PATCH v3 2/7 mm/page_owner: add MR_NEVER to enum migrate_reason and use it for last_migrate_reason

In reply to Ye Liu

The last_migrate_reason field uses -1 as a sentinel value to mean "no migration has happened". Replace the four bare -1 occurrences by adding a proper MR_NEVER member to enum migrate_reason, defining a corresponding "never_migrated" string in the MIGRATE_REASON trace macro, and removing the local MIGRATE_REASON_NONE define.

No functional change.

---
 include/linux/migrate_mode.h   | 1 +
 include/trace/events/migrate.h | 3 ++-
 mm/page_owner.c                | 8 ++++----
 3 files changed, 7 insertions(+), 5 deletions(-)
include/linux/migrate_mode.h +1 -0
index 265c4328b36a..05102d4d2490 100644
@@ -25,6 +25,7 @@ enum migrate_reason {
 	MR_LONGTERM_PIN,
 	MR_DEMOTION,
 	MR_DAMON,
+	MR_NEVER,		/* page has never been migrated */
 	MR_TYPES
 };
 
include/trace/events/migrate.h +2 -1
index cd01dd7b3640..11bc0aa14c7e 100644
@@ -23,7 +23,8 @@
 	EM( MR_CONTIG_RANGE,	"contig_range")			\
 	EM( MR_LONGTERM_PIN,	"longterm_pin")			\
 	EM( MR_DEMOTION,	"demotion")			\
-	EMe(MR_DAMON,		"damon")
+	EM( MR_DAMON,		"damon")			\
+	EMe(MR_NEVER,		"never_migrated")
 
 /*
  * First define the enums in the above macros to be exported to userspace
mm/page_owner.c +4 -4
index 342549891a8d..c2f43ab860eb 100644
@@ -339,7 +339,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, MR_NEVER,
 				   ts_nsec, current->pid, current->tgid,
 				   current->comm);
 	inc_stack_record_count(handle, gfp_mask, 1 << order);
@@ -596,7 +596,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 != MR_NEVER) {
 		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 +667,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 != MR_NEVER)
 		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 +826,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,
+						   MR_NEVER, local_clock(), current->pid,
 						   current->tgid, current->comm);
 			count++;
 ext_put_continue:
Signature · 1 line
2.43.0
reviewer Reviewed-by on v3 2/7
#3 lore raw

Re: [PATCH v3 2/7] mm/page_owner: add MR_NEVER to enum migrate_reason and use it for last_migrate_reason

Quoted 6 lines · Ye Liu

The last_migrate_reason field uses -1 as a sentinel value to mean "no migration has happened". Replace the four bare -1 occurrences by adding a proper MR_NEVER member to enum migrate_reason, defining a corresponding "never_migrated" string in the MIGRATE_REASON trace macro, and removing the local MIGRATE_REASON_NONE define.

Quoted 9 lines

No functional change.

---
 include/linux/migrate_mode.h   | 1 +
 include/trace/events/migrate.h | 3 ++-
 mm/page_owner.c                | 8 ++++----
 3 files changed, 7 insertions(+), 5 deletions(-)

LGTM. Thanks.

Signature · 2 lines
Best Regards,
Yan, Zi
maintainer Reviewed-by on v3 2/7
#4 lore raw

Re: [PATCH v3 2/7] mm/page_owner: add MR_NEVER to enum migrate_reason and use it for last_migrate_reason

In reply to Ye Liu
Quoted 9 lines · Ye Liu

The last_migrate_reason field uses -1 as a sentinel value to mean "no migration has happened". Replace the four bare -1 occurrences by adding a proper MR_NEVER member to enum migrate_reason, defining a corresponding "never_migrated" string in the MIGRATE_REASON trace macro, and removing the local MIGRATE_REASON_NONE define.

No functional change.

Quoted 72 lines
---
 include/linux/migrate_mode.h   | 1 +
 include/trace/events/migrate.h | 3 ++-
 mm/page_owner.c                | 8 ++++----
 3 files changed, 7 insertions(+), 5 deletions(-)
include/linux/migrate_mode.h +1 -0
index 265c4328b36a..05102d4d2490 100644
@@ -25,6 +25,7 @@ enum migrate_reason {
 	MR_LONGTERM_PIN,
 	MR_DEMOTION,
 	MR_DAMON,
+	MR_NEVER,		/* page has never been migrated */
 	MR_TYPES
 };
 
include/trace/events/migrate.h +2 -1
index cd01dd7b3640..11bc0aa14c7e 100644
@@ -23,7 +23,8 @@
 	EM( MR_CONTIG_RANGE,	"contig_range")			\
 	EM( MR_LONGTERM_PIN,	"longterm_pin")			\
 	EM( MR_DEMOTION,	"demotion")			\
-	EMe(MR_DAMON,		"damon")
+	EM( MR_DAMON,		"damon")			\
+	EMe(MR_NEVER,		"never_migrated")
 
 /*
  * First define the enums in the above macros to be exported to userspace
mm/page_owner.c +4 -4
index 342549891a8d..c2f43ab860eb 100644
@@ -339,7 +339,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, MR_NEVER,
 				   ts_nsec, current->pid, current->tgid,
 				   current->comm);
 	inc_stack_record_count(handle, gfp_mask, 1 << order);
@@ -596,7 +596,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 != MR_NEVER) {
 		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 +667,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 != MR_NEVER)
 		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 +826,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,
+						   MR_NEVER, local_clock(), current->pid,
 						   current->tgid, current->comm);
 			count++;
 ext_put_continue:
author
#5 lore raw

PATCH v3 3/7 mm: use enum migrate_reason instead of int for migration reason parameters

In reply to Ye Liu

Replace all 'int reason' function parameters that carry migrate_reason values with the proper 'enum migrate_reason' type. This makes the intent explicit and leverages compiler type checking. The affected subsystems are:

- page_owner: __folio_set_owner_migrate_reason(), folio_set_owner_migrate_reason()
- migrate: migrate_pages(), migrate_pages_sync(), migrate_pages_batch(), migrate_folios_move(), migrate_hugetlbs(), unmap_and_move_huge_page()
- hugetlb: move_hugetlb_state(), htlb_allow_alloc_fallback()
- trace: mm_migrate_pages and mm_migrate_pages_start events

The 'short last_migrate_reason' struct field and internal helper parameter in page_owner are intentionally left as 'short' since they store per-page metadata where size matters.

No functional change.

---
 include/linux/hugetlb.h        |  9 +++++----
 include/linux/migrate.h        |  6 ++++--
 include/linux/page_owner.h     |  7 ++++---
 include/trace/events/migrate.h |  8 ++++----
 mm/hugetlb.c                   |  3 ++-
 mm/migrate.c                   | 12 ++++++------
 mm/page_owner.c                |  2 +-
 7 files changed, 26 insertions(+), 21 deletions(-)
include/linux/hugetlb.h +5 -4
index 2abaf99321e9..fa828232dfcc 100644
@@ -154,7 +154,8 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
 bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list);
 int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison);
 void folio_putback_hugetlb(struct folio *folio);
-void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason);
+void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,
+			enum migrate_reason reason);
 void hugetlb_fix_reserve_counts(struct inode *inode);
 extern struct mutex *hugetlb_fault_mutex_table;
 u32 hugetlb_fault_mutex_hash(struct address_space *mapping, pgoff_t idx);
@@ -424,7 +425,7 @@ static inline void folio_putback_hugetlb(struct folio *folio)
 }
 
 static inline void move_hugetlb_state(struct folio *old_folio,
-					struct folio *new_folio, int reason)
+					struct folio *new_folio, enum migrate_reason reason)
 {
 }
 
@@ -956,7 +957,7 @@ static inline gfp_t htlb_modify_alloc_mask(struct hstate *h, gfp_t gfp_mask)
 	return modified_mask;
 }
 
-static inline bool htlb_allow_alloc_fallback(int reason)
+static inline bool htlb_allow_alloc_fallback(enum migrate_reason reason)
 {
 	bool allowed_fallback = false;
 
@@ -1238,7 +1239,7 @@ static inline gfp_t htlb_modify_alloc_mask(struct hstate *h, gfp_t gfp_mask)
 	return 0;
 }
 
-static inline bool htlb_allow_alloc_fallback(int reason)
+static inline bool htlb_allow_alloc_fallback(enum migrate_reason reason)
 {
 	return false;
 }
include/linux/migrate.h +4 -2
index d5af2b7f577b..1f83924615d6 100644
@@ -57,7 +57,8 @@ void putback_movable_pages(struct list_head *l);
 int migrate_folio(struct address_space *mapping, struct folio *dst,
 		struct folio *src, enum migrate_mode mode);
 int migrate_pages(struct list_head *l, new_folio_t new, free_folio_t free,
-		  unsigned long private, enum migrate_mode mode, int reason,
+		  unsigned long private, enum migrate_mode mode,
+		  enum migrate_reason reason,
 		  unsigned int *ret_succeeded);
 struct folio *alloc_migration_target(struct folio *src, unsigned long private);
 bool isolate_movable_ops_page(struct page *page, isolate_mode_t mode);
@@ -77,7 +78,8 @@ int set_movable_ops(const struct movable_operations *ops, enum pagetype type);
 static inline void putback_movable_pages(struct list_head *l) {}
 static inline int migrate_pages(struct list_head *l, new_folio_t new,
 		free_folio_t free, unsigned long private,
-		enum migrate_mode mode, int reason, unsigned int *ret_succeeded)
+		enum migrate_mode mode, enum migrate_reason reason,
+		unsigned int *ret_succeeded)
 	{ return -ENOSYS; }
 static inline struct folio *alloc_migration_target(struct folio *src,
 		unsigned long private)
include/linux/page_owner.h +4 -3
index 3328357f6dba..9fe51dfccf26 100644
@@ -3,6 +3,7 @@
 #define __LINUX_PAGE_OWNER_H
 
 #include <linux/jump_label.h>
+#include <linux/migrate_mode.h>
 
 #ifdef CONFIG_PAGE_OWNER
 extern struct static_key_false page_owner_inited;
@@ -14,7 +15,7 @@ extern void __set_page_owner(struct page *page,
 extern void __split_page_owner(struct page *page, int old_order,
 			int new_order);
 extern void __folio_copy_owner(struct folio *newfolio, struct folio *old);
-extern void __folio_set_owner_migrate_reason(struct folio *folio, int reason);
+extern void __folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason);
 extern void __dump_page_owner(const struct page *page);
 extern void pagetypeinfo_showmixedcount_print(struct seq_file *m,
 					pg_data_t *pgdat, struct zone *zone);
@@ -43,7 +44,7 @@ static inline void folio_copy_owner(struct folio *newfolio, struct folio *old)
 	if (static_branch_unlikely(&page_owner_inited))
 		__folio_copy_owner(newfolio, old);
 }
-static inline void folio_set_owner_migrate_reason(struct folio *folio, int reason)
+static inline void folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)
 {
 	if (static_branch_unlikely(&page_owner_inited))
 		__folio_set_owner_migrate_reason(folio, reason);
@@ -68,7 +69,7 @@ static inline void split_page_owner(struct page *page, int old_order,
 static inline void folio_copy_owner(struct folio *newfolio, struct folio *folio)
 {
 }
-static inline void folio_set_owner_migrate_reason(struct folio *folio, int reason)
+static inline void folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)
 {
 }
 static inline void dump_page_owner(const struct page *page)
include/trace/events/migrate.h +4 -4
index 11bc0aa14c7e..15ee2ef201b5 100644
@@ -52,7 +52,7 @@ TRACE_EVENT(mm_migrate_pages,
 	TP_PROTO(unsigned long succeeded, unsigned long failed,
 		 unsigned long thp_succeeded, unsigned long thp_failed,
 		 unsigned long thp_split, unsigned long large_folio_split,
-		 enum migrate_mode mode, int reason),
+		 enum migrate_mode mode, enum migrate_reason reason),
 
 	TP_ARGS(succeeded, failed, thp_succeeded, thp_failed,
 		thp_split, large_folio_split, mode, reason),
@@ -65,7 +65,7 @@ TRACE_EVENT(mm_migrate_pages,
 		__field(	unsigned long,		thp_split)
 		__field(	unsigned long,		large_folio_split)
 		__field(	enum migrate_mode,	mode)
-		__field(	int,			reason)
+		__field(	enum migrate_reason,	reason)
 	),
 
 	TP_fast_assign(
@@ -92,13 +92,13 @@ TRACE_EVENT(mm_migrate_pages,
 
 TRACE_EVENT(mm_migrate_pages_start,
 
-	TP_PROTO(enum migrate_mode mode, int reason),
+	TP_PROTO(enum migrate_mode mode, enum migrate_reason reason),
 
 	TP_ARGS(mode, reason),
 
 	TP_STRUCT__entry(
 		__field(enum migrate_mode, mode)
-		__field(int, reason)
+		__field(enum migrate_reason, reason)
 	),
 
 	TP_fast_assign(
mm/hugetlb.c +2 -1
index 571212b80835..17732d1fdc5e 100644
@@ -7182,7 +7182,8 @@ void folio_putback_hugetlb(struct folio *folio)
 	folio_put(folio);
 }
 
-void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason)
+void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,
+			enum migrate_reason reason)
 {
 	struct hstate *h = folio_hstate(old_folio);
 
mm/migrate.c +6 -6
index d9b23909d716..49e10feeb094 100644
@@ -1469,7 +1469,7 @@ static int migrate_folio_move(free_folio_t put_new_folio, unsigned long private,
 static int unmap_and_move_huge_page(new_folio_t get_new_folio,
 		free_folio_t put_new_folio, unsigned long private,
 		struct folio *src, int force, enum migrate_mode mode,
-		int reason, struct list_head *ret)
+		enum migrate_reason reason, struct list_head *ret)
 {
 	struct folio *dst;
 	int rc = -EAGAIN;
@@ -1626,7 +1626,7 @@ struct migrate_pages_stats {
  */
 static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,
 			    free_folio_t put_new_folio, unsigned long private,
-			    enum migrate_mode mode, int reason,
+			    enum migrate_mode mode, enum migrate_reason reason,
 			    struct migrate_pages_stats *stats,
 			    struct list_head *ret_folios)
 {
@@ -1716,7 +1716,7 @@ static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,
 static void migrate_folios_move(struct list_head *src_folios,
 		struct list_head *dst_folios,
 		free_folio_t put_new_folio, unsigned long private,
-		enum migrate_mode mode, int reason,
+		enum migrate_mode mode, enum migrate_reason reason,
 		struct list_head *ret_folios,
 		struct migrate_pages_stats *stats,
 		int *retry, int *thp_retry, int *nr_failed,
@@ -1799,7 +1799,7 @@ static void migrate_folios_undo(struct list_head *src_folios,
  */
 static int migrate_pages_batch(struct list_head *from,
 		new_folio_t get_new_folio, free_folio_t put_new_folio,
-		unsigned long private, enum migrate_mode mode, int reason,
+		unsigned long private, enum migrate_mode mode, enum migrate_reason reason,
 		struct list_head *ret_folios, struct list_head *split_folios,
 		struct migrate_pages_stats *stats, int nr_pass)
 {
@@ -2011,7 +2011,7 @@ static int migrate_pages_batch(struct list_head *from,
 
 static int migrate_pages_sync(struct list_head *from, new_folio_t get_new_folio,
 		free_folio_t put_new_folio, unsigned long private,
-		enum migrate_mode mode, int reason,
+		enum migrate_mode mode, enum migrate_reason reason,
 		struct list_head *ret_folios, struct list_head *split_folios,
 		struct migrate_pages_stats *stats)
 {
@@ -2088,7 +2088,7 @@ static int migrate_pages_sync(struct list_head *from, new_folio_t get_new_folio,
  */
 int migrate_pages(struct list_head *from, new_folio_t get_new_folio,
 		free_folio_t put_new_folio, unsigned long private,
-		enum migrate_mode mode, int reason, unsigned int *ret_succeeded)
+		enum migrate_mode mode, enum migrate_reason reason, unsigned int *ret_succeeded)
 {
 	int rc, rc_gather;
 	int nr_pages;
mm/page_owner.c +1 -1
index c2f43ab860eb..4e352941a6e2 100644
@@ -345,7 +345,7 @@ noinline void __set_page_owner(struct page *page, unsigned short order,
 	inc_stack_record_count(handle, gfp_mask, 1 << order);
 }
 
-void __folio_set_owner_migrate_reason(struct folio *folio, int reason)
+void __folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)
 {
 	struct page_ext *page_ext = page_ext_get(&folio->page);
 	struct page_owner *page_owner;
Signature · 1 line
2.43.0
reviewer Reviewed-by on v3 3/7
#6 lore raw

Re: [PATCH v3 3/7] mm: use enum migrate_reason instead of int for migration reason parameters

Quoted 20 lines · Ye Liu

Replace all 'int reason' function parameters that carry migrate_reason values with the proper 'enum migrate_reason' type. This makes the intent explicit and leverages compiler type checking. The affected subsystems are:

- page_owner: __folio_set_owner_migrate_reason(), folio_set_owner_migrate_reason()
- migrate: migrate_pages(), migrate_pages_sync(), migrate_pages_batch(), migrate_folios_move(), migrate_hugetlbs(), unmap_and_move_huge_page()
- hugetlb: move_hugetlb_state(), htlb_allow_alloc_fallback()
- trace: mm_migrate_pages and mm_migrate_pages_start events

The 'short last_migrate_reason' struct field and internal helper parameter in page_owner are intentionally left as 'short' since they store per-page metadata where size matters.

No functional change.

Quoted 10 lines
---
 include/linux/hugetlb.h        |  9 +++++----
 include/linux/migrate.h        |  6 ++++--
 include/linux/page_owner.h     |  7 ++++---
 include/trace/events/migrate.h |  8 ++++----
 mm/hugetlb.c                   |  3 ++-
 mm/migrate.c                   | 12 ++++++------
 mm/page_owner.c                |  2 +-
 7 files changed, 26 insertions(+), 21 deletions(-)

LGTM. Thanks.

Signature · 2 lines
Best Regards,
Yan, Zi
maintainer Reviewed-by on v3 3/7
#7 lore raw

Re: [PATCH v3 3/7] mm: use enum migrate_reason instead of int for migration reason parameters

In reply to Ye Liu
Quoted 13 lines · Ye Liu

Replace all 'int reason' function parameters that carry migrate_reason values with the proper 'enum migrate_reason' type. This makes the intent explicit and leverages compiler type checking. The affected subsystems are:

- page_owner: __folio_set_owner_migrate_reason(), folio_set_owner_migrate_reason()
- migrate: migrate_pages(), migrate_pages_sync(), migrate_pages_batch(), migrate_folios_move(), migrate_hugetlbs(), unmap_and_move_huge_page()
- hugetlb: move_hugetlb_state(), htlb_allow_alloc_fallback()
- trace: mm_migrate_pages and mm_migrate_pages_start events

Quoted 7 lines

The 'short last_migrate_reason' struct field and internal helper parameter in page_owner are intentionally left as 'short' since they store per-page metadata where size matters.

No functional change.

author
#8 lore raw

PATCH v3 4/7 mm/page_owner: hoist CONFIG_MEMCG to function level for print_page_owner_memcg()

In reply to Ye Liu

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.

---
 mm/page_owner.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
mm/page_owner.c +8 -2
index 4e352941a6e2..fe2bf2274d8a 100644
@@ -522,13 +522,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;
@@ -556,10 +556,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
author
#9 lore raw

PATCH v3 5/7 mm/page_owner: add missing newline to count_threshold format string

In reply to Ye Liu

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.

---
 mm/page_owner.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
mm/page_owner.c +1 -1
index fe2bf2274d8a..7520718f63f1 100644
@@ -974,7 +974,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
author
#10 lore raw

PATCH v3 6/7 mm/page_owner: move free_ts_nsec output to free section in __dump_page_owner()

In reply to Ye Liu

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.

---
 mm/page_owner.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
mm/page_owner.c +5 -4
index 7520718f63f1..84eb44459478 100644
@@ -653,10 +653,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)
@@ -668,8 +668,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
author
#11 lore raw

PATCH v3 7/7 mm/page_owner: drop redundant page_owner prefix from static symbols

In reply to Ye Liu

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.

---
 mm/page_owner.c | 34 ++++++++++++++++------------------
 1 file changed, 16 insertions(+), 18 deletions(-)
mm/page_owner.c +16 -18
index 84eb44459478..46a933f9c229 100644
@@ -894,7 +894,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)
 {
@@ -911,7 +911,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) {
@@ -933,16 +933,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) {
@@ -955,28 +955,26 @@ 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)
 {
@@ -992,17 +990,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