Re: [RFC v1 02/10] powerpc: book3s64: Fix unmap race with PMD THP migration entry

Christophe Leroy (CS GROUP)
2026-03-04T08:54:42Z

Le 25/02/2026 à 12:04, Ritesh Harjani (IBM) a écrit :
> The following race is possible with migration swap entries or
> device-private THP entries. e.g. when move_pages is called on a PMD THP
> page, then there maybe an intermediate state, where PMD entry acts as
> a migration swap entry (pmd_present() is true). Then if an munmap
> happens at the same time, then this VM_BUG_ON() can happen in
> pmdp_huge_get_and_clear_full().
>
> This patch fixes that.
>
> Thread A: move_pages() syscall
> add_folio_for_migration()
> mmap_read_lock(mm)
> folio_isolate_lru(folio)
> mmap_read_unlock(mm)
>
> do_move_pages_to_node()
> migrate_pages()
> try_to_migrate_one()
> spin_lock(ptl)
> set_pmd_migration_entry()
> pmdp_invalidate() # PMD: _PAGE_INVALID | _PAGE_PTE | pfn
> set_pmd_at() # PMD: migration swap entry (pmd_present=0)
> spin_unlock(ptl)
> [page copy phase] # <--- RACE WINDOW -->
>
> Thread B: munmap()
> mmap_write_downgrade(mm)
> unmap_vmas() -> zap_pmd_range()
> zap_huge_pmd()
> __pmd_trans_huge_lock()
> pmd_is_huge(): # !pmd_present && !pmd_none -> TRUE (swap entry)
> pmd_lock() -> # spin_lock(ptl), waits for Thread A to release ptl
> pmdp_huge_get_and_clear_full()
> VM_BUG_ON(!pmd_present(*pmdp)) # HITS!
>
> [ 287.738700][ T1867] ------------[ cut here ]------------
> [ 287.743843][ T1867] kernel BUG at arch/powerpc/mm/book3s64/pgtable.c:187!
> cpu 0x0: Vector: 700 (Program Check) at [c00000044037f4f0]
> pc: c000000000094ca4: pmdp_huge_get_and_clear_full+0x6c/0x23c
> lr: c000000000645dec: zap_huge_pmd+0xb0/0x868
> sp: c00000044037f790
> msr: 800000000282b033
> current = 0xc0000004032c1a00
> paca = 0xc000000004fe0000 irqmask: 0x03 irq_happened: 0x09
> pid = 1867, comm = a.out
> kernel BUG at :187!
> Linux version 6.19.0-12136-g14360d4f917c-dirty (powerpc64le-linux-gnu-gcc (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #27 SMP PREEMPT Sun Feb 22 10:38:56 IST 2026
> enter ? for help
> [link register ] c000000000645dec zap_huge_pmd+0xb0/0x868
> [c00000044037f790] c00000044037f7d0 (unreliable)
> [c00000044037f7d0] c000000000645dcc zap_huge_pmd+0x90/0x868
> [c00000044037f840] c0000000005724cc unmap_page_range+0x176c/0x1f40
> [c00000044037fa00] c000000000572ea0 unmap_vmas+0xb0/0x1d8
> [c00000044037fa90] c0000000005af254 unmap_region+0xb4/0x128
> [c00000044037fb50] c0000000005af400 vms_complete_munmap_vmas+0x138/0x310
> [c00000044037fbe0] c0000000005b0f1c do_vmi_align_munmap+0x1ec/0x238
> [c00000044037fd30] c0000000005b3688 __vm_munmap+0x170/0x1f8
> [c00000044037fdf0] c000000000587f74 sys_munmap+0x2c/0x40
> [c00000044037fe10] c000000000032668 system_call_exception+0x128/0x350
> [c00000044037fe50] c00000000000d05c system_call_vectored_common+0x15c/0x2ec
> ---- Exception: 3000 (System Call Vectored) at 0000000010064a2c
> SP (7fff9b1ee9c0) is in userspace
> 0:mon> zh
>
> Fixes: 75358ea359e7c ("powerpc/mm/book3s64: Fix MADV_DONTNEED and parallel page fault race")
> Reported-by: Pavithra Prakash <pavrampu@linux.vnet.ibm.com>
> Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>

Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>

> ---
> arch/powerpc/mm/book3s64/pgtable.c | 19 +++++++++++++++++--
> 1 file changed, 17 insertions(+), 2 deletions(-)
>
> diff --git a/arch/powerpc/mm/book3s64/pgtable.c b/arch/powerpc/mm/book3s64/pgtable.c
> index 4b09c04654a8..359092001670 100644
> --- a/arch/powerpc/mm/book3s64/pgtable.c
> +++ b/arch/powerpc/mm/book3s64/pgtable.c
> @@ -210,8 +210,23 @@ pmd_t pmdp_huge_get_and_clear_full(struct vm_area_struct *vma,
> {
> pmd_t pmd;
> VM_BUG_ON(addr & ~HPAGE_PMD_MASK);
> - VM_BUG_ON((pmd_present(*pmdp) && !pmd_trans_huge(*pmdp)) ||
> - !pmd_present(*pmdp));
> + VM_BUG_ON((pmd_present(*pmdp) && !pmd_trans_huge(*pmdp)));
> +
> + if (!pmd_present(*pmdp)) {
> + /*
> + * Non-present PMDs can be migration entries or device-private
> + * THP entries. Since these are non-present, so there is no TLB
> + * backing. This happens when the address space is being
> + * unmapped zap_huge_pmd(), and we encounter non-present pmds.
> + * So it is safe to just clear the PMDs here. zap_huge_pmd(),
> + * will take care of withdraw of the deposited table.
> + */
> + pmd = pmdp_get(pmdp);
> + pmd_clear(pmdp);
> + page_table_check_pmd_clear(vma->vm_mm, addr, pmd);
> + return pmd;
> + }
> +
> pmd = pmdp_huge_get_and_clear(vma->vm_mm, addr, pmdp);
> /*
> * if it not a fullmm flush, then we can possibly end up converting
> --
> 2.53.0
>