[PATCH v6 0/9] mm/page_owner: misc cleanups

Ye Liu posted 9 patches 1 week, 4 days ago
include/linux/hugetlb.h         |   9 ++-
include/linux/migrate.h         |   8 +-
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                 | 135 ++++++++++++++++++--------------
scripts/gdb/linux/page_owner.py |   4 +-
9 files changed, 109 insertions(+), 81 deletions(-)
[PATCH v6 0/9] mm/page_owner: misc cleanups
Posted by Ye Liu 1 week, 4 days ago
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, adds the corresponding
"never_migrated" string in the MIGRATE_REASON trace macro, and updates
the GDB page_owner script to use MR_NEVER so that lx-dump-page-owner
correctly detects unmigrated pages.

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.

Patch 8 clamps the PFN advance in skip_buddy_pages() at the next
MAX_ORDER_NR_PAGES boundary.  The lockless buddy_order_unsafe() read
can return a garbage order value if the page is concurrently allocated
between the PageBuddy check and the private read, potentially causing
the PFN to advance past the next bounadry whose pfn_valid() check
would have caught an offline memory section.  In read_page_owner(),
which relies solely on boundary-aligned pfn_valid() to guard
pfn_to_page(), this could lead to an unmapped mem_section access.

Patch 9 avoids two TOCTOU issues in print_page_owner_memcg() by
reusing the page->memcg_data snapshot already taken via READ_ONCE at
the top of the function throughout, instead of calling
page_memcg_check() and PageMemcgKmem() which re-read page->memcg_data
locklessly with VM_BUG_ON assertions.  If the page is concurrently
freed and reallocated as a THP tail or slab page between the initial
guards and these later calls, those assertions can fire on
CONFIG_DEBUG_VM=y builds.  The OBJEXTS (slab) case is also simplified
with an early return since objcg != memcg for slabs.

Ye Liu (9):
  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
  mm/page_owner: clamp skip_buddy_pages() PFN advance at
    MAX_ORDER_NR_PAGES boundary
  mm/page_owner: use memcg_data snapshot to avoid TOCTOU in
    print_page_owner_memcg()

 include/linux/hugetlb.h         |   9 ++-
 include/linux/migrate.h         |   8 +-
 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                 | 135 ++++++++++++++++++--------------
 scripts/gdb/linux/page_owner.py |   4 +-
 9 files changed, 109 insertions(+), 81 deletions(-)

---
v6:
 - Patch 3: drop unnecessary 'extern' from __folio_set_owner_migrate_reason()
   declaration in page_owner.h; Adjust the indentation; add Reviewed-by from 
   Lorenzo Stoakes.
 - Patch 8: clarify commit message wording; add Reviewed-by from Zi Yan and
   Vlastimil Babka.
 - Patch 9: rename patch to cover both TOCTOU fixes; also replace
   page_memcg_check(page) with extracting objcg from the memcg_data snapshot
   to fix a second TOCTOU issue; add early return for the MEMCG_DATA_OBJEXTS
   (slab) case.
 - Link: https://lore.kernel.org/all/20260701061101.344679-1-ye.liu@linux.dev/
v5:
 - Place the two patches corresponding to the Close connection, patch8 
   and patch9, together in this part.
 - Close: https://lore.kernel.org/all/20260625014708.87386-1-ye.liu@linux.dev/
 - Link: https://lore.kernel.org/all/20260701012239.315262-1-ye.liu@linux.dev/

v4:
 - Patch 2: also update scripts/gdb/linux/page_owner.py to use MR_NEVER
   instead of the hardcoded -1.
 - https://lore.kernel.org/all/20260630015331.147174-1-ye.liu@linux.dev/

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/20260626020522.28619-1-ye.liu@linux.dev/

v2:
 - Add cover letter (no code changes).
 - Link: https://lore.kernel.org/all/20260623065234.31866-2-ye.liu@linux.dev/
-- 
2.43.0
Re: [PATCH v6 0/9] mm/page_owner: misc cleanups
Posted by Andrew Morton 1 week, 4 days ago
On Tue, 14 Jul 2026 09:50:59 +0800 Ye Liu <ye.liu@linux.dev> wrote:

> 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.

Thanks, I updated mm.git's mm-unstable branch to this version of the
patchset.

> v6:
>  - Patch 3: drop unnecessary 'extern' from __folio_set_owner_migrate_reason()
>    declaration in page_owner.h; Adjust the indentation; add Reviewed-by from 
>    Lorenzo Stoakes.
>  - Patch 8: clarify commit message wording; add Reviewed-by from Zi Yan and
>    Vlastimil Babka.
>  - Patch 9: rename patch to cover both TOCTOU fixes; also replace
>    page_memcg_check(page) with extracting objcg from the memcg_data snapshot
>    to fix a second TOCTOU issue; add early return for the MEMCG_DATA_OBJEXTS
>    (slab) case.
>  - Link: https://lore.kernel.org/all/20260701061101.344679-1-ye.liu@linux.dev/

Here's how v6 altered mm.git:


 include/linux/hugetlb.h    |    4 ++--
 include/linux/migrate.h    |    6 +++---
 include/linux/page_owner.h |    2 +-
 mm/page_owner.c            |    8 ++++++--
 4 files changed, 12 insertions(+), 8 deletions(-)

--- a/include/linux/hugetlb.h~b
+++ a/include/linux/hugetlb.h
@@ -155,7 +155,7 @@ bool folio_isolate_hugetlb(struct folio
 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,
-			enum migrate_reason reason);
+		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);
@@ -425,7 +425,7 @@ static inline void folio_putback_hugetlb
 }
 
 static inline void move_hugetlb_state(struct folio *old_folio,
-					struct folio *new_folio, enum migrate_reason reason)
+		struct folio *new_folio, enum migrate_reason reason)
 {
 }
 
--- a/include/linux/migrate.h~b
+++ a/include/linux/migrate.h
@@ -57,9 +57,9 @@ void putback_movable_pages(struct list_h
 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,
-		  enum migrate_reason reason,
-		  unsigned int *ret_succeeded);
+		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);
 bool isolate_folio_to_list(struct folio *folio, struct list_head *list);
--- a/include/linux/page_owner.h~b
+++ a/include/linux/page_owner.h
@@ -15,7 +15,7 @@ extern void __set_page_owner(struct 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, enum migrate_reason reason);
+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);
--- a/mm/page_owner.c~b
+++ a/mm/page_owner.c
@@ -540,6 +540,7 @@ static inline int print_page_owner_memcg
 					 struct page *page)
 {
 	unsigned long memcg_data;
+	struct obj_cgroup *objcg;
 	struct mem_cgroup *memcg;
 	bool online;
 	char name[80];
@@ -549,11 +550,14 @@ static inline int print_page_owner_memcg
 	if (!memcg_data || PageTail(page))
 		goto out_unlock;
 
-	if (memcg_data & MEMCG_DATA_OBJEXTS)
+	if (memcg_data & MEMCG_DATA_OBJEXTS) {
 		ret += scnprintf(kbuf + ret, count - ret,
 				"Slab cache page\n");
+		goto out_unlock;
+	}
 
-	memcg = page_memcg_check(page);
+	objcg = (void *)(memcg_data & ~OBJEXTS_FLAGS_MASK);
+	memcg = objcg ? obj_cgroup_memcg(objcg) : NULL;
 	if (!memcg)
 		goto out_unlock;
 
_