[PATCH 06/10] mm/madvise: separate huge PMDs from the PTE walk
From: Gregory Price
Date: Tue Sep 22 2026 - 19:59:32 EST
The PMD callback still owns huge-PMD locking, splitting and reclaim along
with its PTE-table loop. This obscures the transition between the two
paths.
Move huge-PMD lock ownership into a dedicated helper. Return false after a
requested PMD split succeeds so the callback continues at PTE level. Use
the PMD boundary already supplied by walk_pmd_range().
No functional change intended.
Assisted-by: LLM
Signed-off-by: Gregory Price (Meta) <gourry@xxxxxxxxxx>
---
mm/madvise.c | 85 +++++++++++++++++++++++++++++-----------------------
1 file changed, 48 insertions(+), 37 deletions(-)
diff --git a/mm/madvise.c b/mm/madvise.c
index 6b518f7f73651..83b27258c9673 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -435,6 +435,52 @@ madvise_lru_huge_pmd_locked(pmd_t *pmd, pmd_t orig_pmd,
madvise_lru_folio(folio, private->pageout, folio_list);
return NULL;
}
+
+/* Return false when a requested split requires a PTE walk. */
+static bool madvise_lru_huge_pmd(pmd_t *pmd, unsigned long addr,
+ unsigned long next, struct mm_walk *walk,
+ bool pageout_anon_only)
+{
+ const struct madvise_walk_private *private = walk->private;
+ struct mmu_gather *tlb = private->tlb;
+ bool pageout = private->pageout;
+ LIST_HEAD(folio_list);
+ struct folio *folio = NULL;
+ spinlock_t *ptl;
+ pmd_t orig_pmd;
+
+ tlb_change_page_size(tlb, HPAGE_PMD_SIZE);
+ ptl = pmd_trans_huge_lock(pmd, walk->vma);
+ if (!ptl)
+ return true;
+
+ orig_pmd = *pmd;
+ if (unlikely(!pmd_present(orig_pmd))) {
+ VM_WARN_ON_ONCE(!pmd_is_valid_softleaf(orig_pmd));
+ } else {
+ folio = madvise_lru_huge_pmd_locked(pmd, orig_pmd, addr, next,
+ walk, &folio_list, pageout_anon_only);
+ }
+ spin_unlock(ptl);
+
+ if (folio) {
+ int err = split_folio(folio);
+
+ folio_unlock(folio);
+ folio_put(folio);
+ return err != 0;
+ }
+ if (pageout)
+ reclaim_pages(&folio_list);
+ return true;
+}
+#else
+static bool madvise_lru_huge_pmd(pmd_t *pmd, unsigned long addr,
+ unsigned long next, struct mm_walk *walk,
+ bool pageout_anon_only)
+{
+ return false;
+}
#endif
static int madvise_lru_pmd_entry(pmd_t *pmd, unsigned long addr,
@@ -458,44 +504,9 @@ static int madvise_lru_pmd_entry(pmd_t *pmd, unsigned long addr,
pageout_anon_only = pageout && !vma_is_anonymous(vma) &&
!can_do_file_pageout(vma);
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
- if (pmd_trans_huge(*pmd)) {
- pmd_t orig_pmd;
- unsigned long next = pmd_addr_end(addr, end);
-
- tlb_change_page_size(tlb, HPAGE_PMD_SIZE);
- ptl = pmd_trans_huge_lock(pmd, vma);
- if (!ptl)
- return 0;
-
- orig_pmd = *pmd;
- if (unlikely(!pmd_present(orig_pmd))) {
- VM_WARN_ON_ONCE(!pmd_is_valid_softleaf(orig_pmd));
- goto huge_unlock;
- }
-
- folio = madvise_lru_huge_pmd_locked(pmd, orig_pmd, addr, next,
- walk, &folio_list, pageout_anon_only);
- if (folio) {
- int err;
-
- spin_unlock(ptl);
- err = split_folio(folio);
- folio_unlock(folio);
- folio_put(folio);
- if (!err)
- goto regular_folio;
- return 0;
- }
-huge_unlock:
- spin_unlock(ptl);
- if (pageout)
- reclaim_pages(&folio_list);
+ if (pmd_trans_huge(*pmd) &&
+ madvise_lru_huge_pmd(pmd, addr, end, walk, pageout_anon_only))
return 0;
- }
-
-regular_folio:
-#endif
tlb_change_page_size(tlb, PAGE_SIZE);
restart:
start_pte = pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
--
2.53.0-Meta