[PATCH RFC 6/9] mm/huge_memory: add and use split_huge_pmd_anon_rmap()
From: Yin Tirui
Date: Fri Aug 28 2026 - 14:43:16 EST
The anon-exclusive handling and the PTE-level rmap conversion are written
twice, once for present entries and once for device private ones. Factor
them into one helper.
It returns whether the mapping may still be frozen instead of writing the
caller's freeze back.
No functional change intended.
Signed-off-by: Yin Tirui <yintirui@xxxxxxxxx>
---
mm/huge_memory.c | 90 +++++++++++++++++++++++++-----------------------
1 file changed, 47 insertions(+), 43 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 1cd8878edc1d..e0083a9e89b8 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -3194,6 +3194,43 @@ static void unmap_huge_pmd_entry(struct vm_area_struct *vma,
folio_put(folio);
}
+/*
+ * Convert the folio's PMD-level anonymous rmap into PTE-level ones.
+ *
+ * Without "freeze", we'll simply split the PMD, propagating the
+ * PageAnonExclusive() flag for each PTE by setting it for
+ * each subpage -- no need to (temporarily) clear.
+ *
+ * With "freeze" we want to replace mapped pages by
+ * migration entries right away. This is only possible if we
+ * managed to clear PageAnonExclusive() -- see
+ * set_pmd_migration_entry().
+ *
+ * In case we cannot clear PageAnonExclusive(), split the PMD
+ * only and let try_to_migrate_one() fail later.
+ *
+ * See folio_try_share_anon_rmap_pmd(): invalidate PMD first.
+ *
+ * Returns: whether the mapping may still be frozen.
+ */
+static bool split_huge_pmd_anon_rmap(struct folio *folio, struct page *page,
+ struct vm_area_struct *vma, unsigned long haddr, bool freeze,
+ bool anon_exclusive)
+{
+ rmap_t rmap_flags = RMAP_NONE;
+
+ if (freeze &&
+ (!anon_exclusive || !folio_try_share_anon_rmap_pmd(folio, page)))
+ return true;
+
+ folio_ref_add(folio, HPAGE_PMD_NR - 1);
+ if (anon_exclusive)
+ rmap_flags |= RMAP_EXCLUSIVE;
+ folio_add_anon_rmap_ptes(folio, page, HPAGE_PMD_NR, vma, haddr,
+ rmap_flags);
+ return false;
+}
+
static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
unsigned long haddr, bool freeze)
{
@@ -3275,23 +3312,12 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
anon_exclusive = PageAnonExclusive(page);
/*
- * Device private THP should be treated the same as regular
- * folios w.r.t anon exclusive handling. See the comments for
- * folio handling and anon_exclusive below.
+ * Device private folios are treated the same as regular folios
+ * w.r.t. anon exclusive handling, see
+ * split_huge_pmd_anon_rmap().
*/
- if (freeze && anon_exclusive &&
- folio_try_share_anon_rmap_pmd(folio, page))
- freeze = false;
- if (!freeze) {
- rmap_t rmap_flags = RMAP_NONE;
-
- folio_ref_add(folio, HPAGE_PMD_NR - 1);
- if (anon_exclusive)
- rmap_flags |= RMAP_EXCLUSIVE;
-
- folio_add_anon_rmap_ptes(folio, page, HPAGE_PMD_NR,
- vma, haddr, rmap_flags);
- }
+ freeze = split_huge_pmd_anon_rmap(folio, page, vma, haddr,
+ freeze, anon_exclusive);
} else {
/*
* Up to this point the pmd is present and huge and userland has
@@ -3315,6 +3341,9 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
* complete for this pmd), then we flush the SMP TLB and finally
* we write the non-huge version of the pmd entry with
* pmd_populate.
+ *
+ * This must also happen before PageAnonExclusive() is read
+ * below, see folio_try_share_anon_rmap_pmd().
*/
old_pmd = pmdp_invalidate(vma, haddr, pmd);
page = pmd_page(old_pmd);
@@ -3329,34 +3358,9 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
VM_WARN_ON_FOLIO(!folio_ref_count(folio), folio);
- /*
- * Without "freeze", we'll simply split the PMD, propagating the
- * PageAnonExclusive() flag for each PTE by setting it for
- * each subpage -- no need to (temporarily) clear.
- *
- * With "freeze" we want to replace mapped pages by
- * migration entries right away. This is only possible if we
- * managed to clear PageAnonExclusive() -- see
- * set_pmd_migration_entry().
- *
- * In case we cannot clear PageAnonExclusive(), split the PMD
- * only and let try_to_migrate_one() fail later.
- *
- * See folio_try_share_anon_rmap_pmd(): invalidate PMD first.
- */
anon_exclusive = PageAnonExclusive(page);
- if (freeze && anon_exclusive &&
- folio_try_share_anon_rmap_pmd(folio, page))
- freeze = false;
- if (!freeze) {
- rmap_t rmap_flags = RMAP_NONE;
-
- folio_ref_add(folio, HPAGE_PMD_NR - 1);
- if (anon_exclusive)
- rmap_flags |= RMAP_EXCLUSIVE;
- folio_add_anon_rmap_ptes(folio, page, HPAGE_PMD_NR,
- vma, haddr, rmap_flags);
- }
+ freeze = split_huge_pmd_anon_rmap(folio, page, vma, haddr,
+ freeze, anon_exclusive);
}
/*
--
2.34.1