Re: [RESEND v7 11/29] mm: split PMD swap entries into PTE swap entries
From: Kiryl Shutsemau
Date: Wed Sep 16 2026 - 11:13:35 EST
On Mon, Sep 14, 2026 at 05:28:01AM -0700, Usama Arif wrote:
> Once a PMD can hold a swap entry, everything that splits a PMD - mprotect()
> or munmap() over part of the range, MADV_FREE, a pagewalk with no PMD
> handler - has to be able to split that entry too, or the callers that rely
> on split_huge_pmd() to hand them a PTE table would find the PMD unchanged.
>
> No reference counting is needed: a swap entry pins no folio, and swap_map
> is already one per slot, so the PTEs simply take over what the PMD held.
>
> The migration-only entry point cannot reach the new branch, because
> page_vma_mapped_walk() never hands back a swap PMD for the folio being
> migrated. Warn if that ever changes, and force the regular split anyway,
> since the branch leaves folio and page uninitialised.
>
> Test the pre-split old_pmd rather than re-reading *pmd in the trailing
> folio_remove_rmap_pmd() gate, so every entry-type test in the function
> interrogates the same snapshot. That part is cosmetic: pmdp_invalidate()
> leaves the PMD present as far as software is concerned.
>
> Signed-off-by: Usama Arif <usama.arif@xxxxxxxxx>
> ---
> mm/huge_memory.c | 36 +++++++++++++++++++++++++++++++++++-
> 1 file changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 873887aed0bc2..0e347a545588c 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -3304,6 +3304,21 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
> folio_add_anon_rmap_ptes(folio, page, HPAGE_PMD_NR,
> vma, haddr, rmap_flags);
> }
> + } else if (pmd_is_swap_entry(*pmd)) {
> + /*
> + * A PMD swap entry has no page, so it cannot be turned into
> + * PTE migration entries. page_vma_mapped_walk() never hands
> + * one back for the folio being migrated, so this should not
> + * happen; warn, but also force the regular split so that a
> + * broken invariant cannot make the code below dereference the
> + * uninitialised folio and page.
> + */
The comment can be shorter.
> + VM_WARN_ON_ONCE(use_migration_entries);
> + use_migration_entries = false;
> + old_pmd = *pmd;
> + soft_dirty = pmd_swp_soft_dirty(old_pmd);
> + uffd_wp = pmd_swp_uffd(old_pmd);
> + anon_exclusive = pmd_swp_exclusive(old_pmd);
The logic looks right to me, but __split_huge_pmd_locked() is getting
awkward. It is close to 300 lines with two if-else chains that have to
be kept in sync.
Can we have a preparatory patch that moves the PTE-install loops into
per-type helpers?
split_pmd_into_migration_ptes(), split_pmd_into_device_private_ptes(),
split_pmd_into_present_ptes().
Each is a plain loop with set_pte_at() or set_ptes(), so it is pure
code motion.
This patch would then add split_pmd_into_swap_ptes(), which visibly
takes no page or folio, and the comment goes away.
--
Kiryl Shutsemau / Kirill A. Shutemov