* [PATCH] mm: synchronize with PTE table reclaim for all folio sizes
@ 2026-11-05 2:56 Baolin Wang
2026-11-05 3:22 ` Qi Zheng
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Baolin Wang @ 2026-11-05 2:56 UTC (permalink / raw)
To: akpm, david, ljs
Cc: riel, liam, vbabka, harry, jannh, lance.yang, qi.zheng, pfalcato,
ayushr, baolin.wang, linux-mm, linux-kernel
Similar to commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped
folios"), the '!pmd_present()' check in page_vma_mapped_walk() can also race
with concurrent page table reclamation via zap_empty_pte_table().
page_vma_mapped_walk() is used by many functions including try_to_unmap(),
try_to_migrate(), folio_mkclean(), pfn_mkclean_range(), remove_migration_ptes(),
page_mapped_in_vma(), and mapping_wrprotect_range(). Except for try_to_unmap()
and try_to_migrate(), the other callers do not perform post-walk accounting
checks, so the race has no observable impact on them.
For migration, the race can cause unnecessary retries. For reclaim via
try_to_unmap(), the race can cause failure to reclaim a folio that is in
fact already unmapped (as shown below), which may lead to unexpected OOM
or increased refaults.
CPU 0 (reclaim) CPU 1 (MADV_DONTNEED)
-------------------------------- --------------------------------
folio_lock(F)
try_to_unmap(F, TTU_SYNC)
zap_pte_range(full PMD range)
spin_lock(ptl)
clear PTEs
zap_empty_pte_table()
spin_trylock(pml)
pmd_clear()
spin_unlock(pml)
page_vma_mapped_walk()
observe !pmd_present(pmde)
nr_pages < HPAGE_PMD_NR
skip synchronization
return false
try_to_unmap() returns
folio_mapped(F) == true
nr_unmap_fail += folio_nr_pages(F)
tlb_flush_mmu_tlbonly()
tlb_flush_rmaps()
remove F's rmaps
spin_unlock(ptl)
Commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped folios")
has already moved the PMD clear operation (via zap_empty_pte_table()) to
after tlb_flush_rmaps(). Similarly, page_vma_mapped_walk() also needs to
use sync_with_folio_pmd_zap() to synchronize and avoid memory ordering
issues on weakly ordered CPUs.
Fixes: 6375e95f381e ("mm: pgtable: reclaim empty PTE page in madvise(MADV_DONTNEED)")
Cc: stable@vger.kernel.org
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com>
---
mm/page_vma_mapped.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c
index e6b6170c2f4f..8797467b6f0c 100644
--- a/mm/page_vma_mapped.c
+++ b/mm/page_vma_mapped.c
@@ -293,10 +293,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
spin_unlock(pvmw->ptl);
pvmw->ptl = NULL;
} else if (!pmd_present(pmde)) {
- if ((pvmw->flags & PVMW_SYNC) &&
- thp_vma_suitable_order(vma, pvmw->address,
- PMD_ORDER) &&
- (pvmw->nr_pages >= HPAGE_PMD_NR))
+ if (pvmw->flags & PVMW_SYNC)
sync_with_folio_pmd_zap(mm, pvmw->pmd);
goto next_pmd;
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 2:56 [PATCH] mm: synchronize with PTE table reclaim for all folio sizes Baolin Wang @ 2026-11-05 3:22 ` Qi Zheng 2026-11-05 4:54 ` Lance Yang 2026-11-05 11:37 ` Pedro Falcato 2 siblings, 0 replies; 9+ messages in thread From: Qi Zheng @ 2026-11-05 3:22 UTC (permalink / raw) To: Baolin Wang, akpm, david, ljs Cc: riel, liam, vbabka, harry, jannh, lance.yang, pfalcato, ayushr, linux-mm, linux-kernel On 11/5/26 10:56 AM, Baolin Wang wrote: > Similar to commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped > folios"), the '!pmd_present()' check in page_vma_mapped_walk() can also race > with concurrent page table reclamation via zap_empty_pte_table(). > > page_vma_mapped_walk() is used by many functions including try_to_unmap(), > try_to_migrate(), folio_mkclean(), pfn_mkclean_range(), remove_migration_ptes(), > page_mapped_in_vma(), and mapping_wrprotect_range(). Except for try_to_unmap() > and try_to_migrate(), the other callers do not perform post-walk accounting > checks, so the race has no observable impact on them. > > For migration, the race can cause unnecessary retries. For reclaim via > try_to_unmap(), the race can cause failure to reclaim a folio that is in > fact already unmapped (as shown below), which may lead to unexpected OOM > or increased refaults. > > CPU 0 (reclaim) CPU 1 (MADV_DONTNEED) > -------------------------------- -------------------------------- > folio_lock(F) > try_to_unmap(F, TTU_SYNC) > zap_pte_range(full PMD range) > spin_lock(ptl) > clear PTEs > zap_empty_pte_table() > spin_trylock(pml) > pmd_clear() > spin_unlock(pml) > > page_vma_mapped_walk() > observe !pmd_present(pmde) > nr_pages < HPAGE_PMD_NR > skip synchronization > return false > > try_to_unmap() returns > folio_mapped(F) == true > nr_unmap_fail += folio_nr_pages(F) > tlb_flush_mmu_tlbonly() > tlb_flush_rmaps() > remove F's rmaps > spin_unlock(ptl) > > Commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped folios") > has already moved the PMD clear operation (via zap_empty_pte_table()) to > after tlb_flush_rmaps(). Similarly, page_vma_mapped_walk() also needs to > use sync_with_folio_pmd_zap() to synchronize and avoid memory ordering > issues on weakly ordered CPUs. > > Fixes: 6375e95f381e ("mm: pgtable: reclaim empty PTE page in madvise(MADV_DONTNEED)") > Cc: stable@vger.kernel.org > Suggested-by: David Hildenbrand (Arm) <david@kernel.org> > Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com> > --- > mm/page_vma_mapped.c | 5 +---- > 1 file changed, 1 insertion(+), 4 deletions(-) > > diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c > index e6b6170c2f4f..8797467b6f0c 100644 > --- a/mm/page_vma_mapped.c > +++ b/mm/page_vma_mapped.c > @@ -293,10 +293,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) > spin_unlock(pvmw->ptl); > pvmw->ptl = NULL; > } else if (!pmd_present(pmde)) { > - if ((pvmw->flags & PVMW_SYNC) && > - thp_vma_suitable_order(vma, pvmw->address, > - PMD_ORDER) && > - (pvmw->nr_pages >= HPAGE_PMD_NR)) > + if (pvmw->flags & PVMW_SYNC) > sync_with_folio_pmd_zap(mm, pvmw->pmd); > > goto next_pmd; Acked-by: Qi Zheng <qi.zheng@linux.dev> Thanks, Qi ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 2:56 [PATCH] mm: synchronize with PTE table reclaim for all folio sizes Baolin Wang 2026-11-05 3:22 ` Qi Zheng @ 2026-11-05 4:54 ` Lance Yang 2026-11-05 5:48 ` Lance Yang 2026-11-05 11:37 ` Pedro Falcato 2 siblings, 1 reply; 9+ messages in thread From: Lance Yang @ 2026-11-05 4:54 UTC (permalink / raw) To: baolin.wang Cc: akpm, david, ljs, riel, liam, vbabka, harry, jannh, lance.yang, qi.zheng, pfalcato, ayushr, linux-mm, linux-kernel On Thu, Nov 05, 2026 at 10:56:28AM +0800, Baolin Wang wrote: >Similar to commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped >folios"), the '!pmd_present()' check in page_vma_mapped_walk() can also race >with concurrent page table reclamation via zap_empty_pte_table(). Good catch! >page_vma_mapped_walk() is used by many functions including try_to_unmap(), >try_to_migrate(), folio_mkclean(), pfn_mkclean_range(), remove_migration_ptes(), >page_mapped_in_vma(), and mapping_wrprotect_range(). Except for try_to_unmap() >and try_to_migrate(), the other callers do not perform post-walk accounting >checks, so the race has no observable impact on them. > >For migration, the race can cause unnecessary retries. For reclaim via >try_to_unmap(), the race can cause failure to reclaim a folio that is in >fact already unmapped (as shown below), which may lead to unexpected OOM >or increased refaults. > >CPU 0 (reclaim) CPU 1 (MADV_DONTNEED) >-------------------------------- -------------------------------- >folio_lock(F) >try_to_unmap(F, TTU_SYNC) > zap_pte_range(full PMD range) > spin_lock(ptl) > clear PTEs > zap_empty_pte_table() > spin_trylock(pml) > pmd_clear() > spin_unlock(pml) > > page_vma_mapped_walk() > observe !pmd_present(pmde) > nr_pages < HPAGE_PMD_NR > skip synchronization > return false > >try_to_unmap() returns >folio_mapped(F) == true >nr_unmap_fail += folio_nr_pages(F) > tlb_flush_mmu_tlbonly() > tlb_flush_rmaps() > remove F's rmaps > spin_unlock(ptl) > >Commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped folios") >has already moved the PMD clear operation (via zap_empty_pte_table()) to >after tlb_flush_rmaps(). Similarly, page_vma_mapped_walk() also needs to >use sync_with_folio_pmd_zap() to synchronize and avoid memory ordering >issues on weakly ordered CPUs. > >Fixes: 6375e95f381e ("mm: pgtable: reclaim empty PTE page in madvise(MADV_DONTNEED)") >Cc: stable@vger.kernel.org >Suggested-by: David Hildenbrand (Arm) <david@kernel.org> >Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com> >--- > mm/page_vma_mapped.c | 5 +---- > 1 file changed, 1 insertion(+), 4 deletions(-) > >diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c >index e6b6170c2f4f..8797467b6f0c 100644 >--- a/mm/page_vma_mapped.c >+++ b/mm/page_vma_mapped.c >@@ -293,10 +293,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) > spin_unlock(pvmw->ptl); > pvmw->ptl = NULL; > } else if (!pmd_present(pmde)) { >- if ((pvmw->flags & PVMW_SYNC) && >- thp_vma_suitable_order(vma, pvmw->address, >- PMD_ORDER) && >- (pvmw->nr_pages >= HPAGE_PMD_NR)) >+ if (pvmw->flags & PVMW_SYNC) Right. Whether the folio can be PMD-mapped doesn't tell us whether its PTE table can be reclaimed, so those extra checks can skip synchronization that the caller explicitly asked for ;) LGTM, thanks! Reviewed-by: Lance Yang <lance.yang@linux.dev> > sync_with_folio_pmd_zap(mm, pvmw->pmd); > > goto next_pmd; >-- >2.47.3 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 4:54 ` Lance Yang @ 2026-11-05 5:48 ` Lance Yang 2026-11-05 9:00 ` Baolin Wang 0 siblings, 1 reply; 9+ messages in thread From: Lance Yang @ 2026-11-05 5:48 UTC (permalink / raw) To: baolin.wang Cc: akpm, david, ljs, riel, liam, vbabka, harry, jannh, qi.zheng, pfalcato, ayushr, linux-mm, linux-kernel On 2026/11/5 12:54, Lance Yang wrote: > > On Thu, Nov 05, 2026 at 10:56:28AM +0800, Baolin Wang wrote: >> Similar to commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped >> folios"), the '!pmd_present()' check in page_vma_mapped_walk() can also race >> with concurrent page table reclamation via zap_empty_pte_table(). > > Good catch! > >> page_vma_mapped_walk() is used by many functions including try_to_unmap(), >> try_to_migrate(), folio_mkclean(), pfn_mkclean_range(), remove_migration_ptes(), >> page_mapped_in_vma(), and mapping_wrprotect_range(). Except for try_to_unmap() >> and try_to_migrate(), the other callers do not perform post-walk accounting >> checks, so the race has no observable impact on them. >> >> For migration, the race can cause unnecessary retries. For reclaim via >> try_to_unmap(), the race can cause failure to reclaim a folio that is in >> fact already unmapped (as shown below), which may lead to unexpected OOM >> or increased refaults. >> >> CPU 0 (reclaim) CPU 1 (MADV_DONTNEED) >> -------------------------------- -------------------------------- >> folio_lock(F) >> try_to_unmap(F, TTU_SYNC) >> zap_pte_range(full PMD range) >> spin_lock(ptl) >> clear PTEs >> zap_empty_pte_table() >> spin_trylock(pml) >> pmd_clear() >> spin_unlock(pml) >> >> page_vma_mapped_walk() >> observe !pmd_present(pmde) >> nr_pages < HPAGE_PMD_NR >> skip synchronization >> return false >> >> try_to_unmap() returns >> folio_mapped(F) == true >> nr_unmap_fail += folio_nr_pages(F) >> tlb_flush_mmu_tlbonly() >> tlb_flush_rmaps() >> remove F's rmaps >> spin_unlock(ptl) >> >> Commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped folios") >> has already moved the PMD clear operation (via zap_empty_pte_table()) to >> after tlb_flush_rmaps(). Similarly, page_vma_mapped_walk() also needs to >> use sync_with_folio_pmd_zap() to synchronize and avoid memory ordering >> issues on weakly ordered CPUs. >> >> Fixes: 6375e95f381e ("mm: pgtable: reclaim empty PTE page in madvise(MADV_DONTNEED)") >> Cc: stable@vger.kernel.org >> Suggested-by: David Hildenbrand (Arm) <david@kernel.org> >> Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com> >> --- >> mm/page_vma_mapped.c | 5 +---- >> 1 file changed, 1 insertion(+), 4 deletions(-) >> >> diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c >> index e6b6170c2f4f..8797467b6f0c 100644 >> --- a/mm/page_vma_mapped.c >> +++ b/mm/page_vma_mapped.c >> @@ -293,10 +293,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) >> spin_unlock(pvmw->ptl); >> pvmw->ptl = NULL; >> } else if (!pmd_present(pmde)) { >> - if ((pvmw->flags & PVMW_SYNC) && >> - thp_vma_suitable_order(vma, pvmw->address, >> - PMD_ORDER) && >> - (pvmw->nr_pages >= HPAGE_PMD_NR)) >> + if (pvmw->flags & PVMW_SYNC) > > Right. Whether the folio can be PMD-mapped doesn't tell us whether its > PTE table can be reclaimed, so those extra checks can skip synchronization > that the caller explicitly asked for ;) Ouch ... one thing I missed ... what about order-0 reclaim? shrink_folio_list() only sets TTU_SYNC for large folio, so can't we still hit the same race there? Cheers, Lance > LGTM, thanks! > > Reviewed-by: Lance Yang <lance.yang@linux.dev> > >> sync_with_folio_pmd_zap(mm, pvmw->pmd); >> >> goto next_pmd; >> -- >> 2.47.3 >> >> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 5:48 ` Lance Yang @ 2026-11-05 9:00 ` Baolin Wang 2026-11-05 10:06 ` David Hildenbrand (Arm) 0 siblings, 1 reply; 9+ messages in thread From: Baolin Wang @ 2026-11-05 9:00 UTC (permalink / raw) To: Lance Yang, Barry Song Cc: akpm, david, ljs, riel, liam, vbabka, harry, jannh, qi.zheng, pfalcato, ayushr, linux-mm, linux-kernel CC Barry, who added the TTU_SYNC in shrink_folio_list(). On 11/5/26 1:48 PM, Lance Yang wrote: > > > On 2026/11/5 12:54, Lance Yang wrote: >> >> On Thu, Nov 05, 2026 at 10:56:28AM +0800, Baolin Wang wrote: >>> Similar to commit 243cf1f67b36 ("mm: memory: fix truncation removing >>> mapped >>> folios"), the '!pmd_present()' check in page_vma_mapped_walk() can >>> also race >>> with concurrent page table reclamation via zap_empty_pte_table(). >> >> Good catch! >> >>> page_vma_mapped_walk() is used by many functions including >>> try_to_unmap(), >>> try_to_migrate(), folio_mkclean(), pfn_mkclean_range(), >>> remove_migration_ptes(), >>> page_mapped_in_vma(), and mapping_wrprotect_range(). Except for >>> try_to_unmap() >>> and try_to_migrate(), the other callers do not perform post-walk >>> accounting >>> checks, so the race has no observable impact on them. >>> >>> For migration, the race can cause unnecessary retries. For reclaim via >>> try_to_unmap(), the race can cause failure to reclaim a folio that is in >>> fact already unmapped (as shown below), which may lead to unexpected OOM >>> or increased refaults. >>> >>> CPU 0 (reclaim) CPU 1 (MADV_DONTNEED) >>> -------------------------------- -------------------------------- >>> folio_lock(F) >>> try_to_unmap(F, TTU_SYNC) >>> zap_pte_range(full PMD range) >>> spin_lock(ptl) >>> clear PTEs >>> zap_empty_pte_table() >>> spin_trylock(pml) >>> pmd_clear() >>> spin_unlock(pml) >>> >>> page_vma_mapped_walk() >>> observe !pmd_present(pmde) >>> nr_pages < HPAGE_PMD_NR >>> skip synchronization >>> return false >>> >>> try_to_unmap() returns >>> folio_mapped(F) == true >>> nr_unmap_fail += folio_nr_pages(F) >>> tlb_flush_mmu_tlbonly() >>> tlb_flush_rmaps() >>> remove F's rmaps >>> spin_unlock(ptl) >>> >>> Commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped >>> folios") >>> has already moved the PMD clear operation (via zap_empty_pte_table()) to >>> after tlb_flush_rmaps(). Similarly, page_vma_mapped_walk() also needs to >>> use sync_with_folio_pmd_zap() to synchronize and avoid memory ordering >>> issues on weakly ordered CPUs. >>> >>> Fixes: 6375e95f381e ("mm: pgtable: reclaim empty PTE page in >>> madvise(MADV_DONTNEED)") >>> Cc: stable@vger.kernel.org >>> Suggested-by: David Hildenbrand (Arm) <david@kernel.org> >>> Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com> >>> --- >>> mm/page_vma_mapped.c | 5 +---- >>> 1 file changed, 1 insertion(+), 4 deletions(-) >>> >>> diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c >>> index e6b6170c2f4f..8797467b6f0c 100644 >>> --- a/mm/page_vma_mapped.c >>> +++ b/mm/page_vma_mapped.c >>> @@ -293,10 +293,7 @@ bool page_vma_mapped_walk(struct >>> page_vma_mapped_walk *pvmw) >>> spin_unlock(pvmw->ptl); >>> pvmw->ptl = NULL; >>> } else if (!pmd_present(pmde)) { >>> - if ((pvmw->flags & PVMW_SYNC) && >>> - thp_vma_suitable_order(vma, pvmw->address, >>> - PMD_ORDER) && >>> - (pvmw->nr_pages >= HPAGE_PMD_NR)) >>> + if (pvmw->flags & PVMW_SYNC) >> >> Right. Whether the folio can be PMD-mapped doesn't tell us whether its >> PTE table can be reclaimed, so those extra checks can skip >> synchronization >> that the caller explicitly asked for ;) > > Ouch ... one thing I missed ... what about order-0 reclaim? > > shrink_folio_list() only sets TTU_SYNC for large folio, so can't we still > hit the same race there? Thanks Lance. But this matches the purpose of the TTU_SYNC flag, which is used to avoid racy checks with PVMW_SYNC. If no TTU_SYNC flag is set, it means the caller can tolerate some racy checks. For this case, I think we should set TTU_SYNC unconditionally in shrink_folio_list(). Barry, what do you think? ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 9:00 ` Baolin Wang @ 2026-11-05 10:06 ` David Hildenbrand (Arm) 2026-11-05 11:18 ` Baolin Wang 2026-11-05 13:22 ` Lance Yang 0 siblings, 2 replies; 9+ messages in thread From: David Hildenbrand (Arm) @ 2026-11-05 10:06 UTC (permalink / raw) To: Baolin Wang, Lance Yang, Barry Song Cc: akpm, ljs, riel, liam, vbabka, harry, jannh, qi.zheng, pfalcato, ayushr, linux-mm, linux-kernel On 11/5/26 10:00, Baolin Wang wrote: > CC Barry, who added the TTU_SYNC in shrink_folio_list(). > > On 11/5/26 1:48 PM, Lance Yang wrote: >> >> >> On 2026/11/5 12:54, Lance Yang wrote: >>> >>> >>> Good catch! >>> >>> >>> Right. Whether the folio can be PMD-mapped doesn't tell us whether its >>> PTE table can be reclaimed, so those extra checks can skip synchronization >>> that the caller explicitly asked for ;) >> >> Ouch ... one thing I missed ... what about order-0 reclaim? >> >> shrink_folio_list() only sets TTU_SYNC for large folio, so can't we still >> hit the same race there? > > Thanks Lance. But this matches the purpose of the TTU_SYNC flag, which is used > to avoid racy checks with PVMW_SYNC. If no TTU_SYNC flag is set, it means the > caller can tolerate some racy checks. > > For this case, I think we should set TTU_SYNC unconditionally in > shrink_folio_list(). Barry, what do you think? That makes sense. I hope this doesn't throw a big wrench into our reclaim performance numbers. Can someone benchmark this? But maybe we should really follow-up with the folio-size based optimization in sync_with_folio_pmd_zap(), where we only need a memory barrier (and sufficient documentation why something that cannot be PMD-mapped can get away cheaper). -- Cheers, David ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 10:06 ` David Hildenbrand (Arm) @ 2026-11-05 11:18 ` Baolin Wang 2026-11-05 13:22 ` Lance Yang 1 sibling, 0 replies; 9+ messages in thread From: Baolin Wang @ 2026-11-05 11:18 UTC (permalink / raw) To: David Hildenbrand (Arm), Lance Yang, Barry Song Cc: akpm, ljs, riel, liam, vbabka, harry, jannh, qi.zheng, pfalcato, ayushr, linux-mm, linux-kernel On 11/5/26 6:06 PM, David Hildenbrand (Arm) wrote: > On 11/5/26 10:00, Baolin Wang wrote: >> CC Barry, who added the TTU_SYNC in shrink_folio_list(). >> >> On 11/5/26 1:48 PM, Lance Yang wrote: >>> >>> >>> On 2026/11/5 12:54, Lance Yang wrote: >>>> >>>> >>>> Good catch! >>>> >>>> >>>> Right. Whether the folio can be PMD-mapped doesn't tell us whether its >>>> PTE table can be reclaimed, so those extra checks can skip synchronization >>>> that the caller explicitly asked for ;) >>> >>> Ouch ... one thing I missed ... what about order-0 reclaim? >>> >>> shrink_folio_list() only sets TTU_SYNC for large folio, so can't we still >>> hit the same race there? >> >> Thanks Lance. But this matches the purpose of the TTU_SYNC flag, which is used >> to avoid racy checks with PVMW_SYNC. If no TTU_SYNC flag is set, it means the >> caller can tolerate some racy checks. >> >> For this case, I think we should set TTU_SYNC unconditionally in >> shrink_folio_list(). Barry, what do you think? > > That makes sense. I hope this doesn't throw a big wrench into our reclaim > performance numbers. > > Can someone benchmark this? Sure. I'll measure it if no one objects to this change. > But maybe we should really follow-up with the folio-size based optimization in > sync_with_folio_pmd_zap(), where we only need a memory barrier (and sufficient > documentation why something that cannot be PMD-mapped can get away cheaper). OK. I'll do this later. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 10:06 ` David Hildenbrand (Arm) 2026-11-05 11:18 ` Baolin Wang @ 2026-11-05 13:22 ` Lance Yang 1 sibling, 0 replies; 9+ messages in thread From: Lance Yang @ 2026-11-05 13:22 UTC (permalink / raw) To: david, baolin.wang Cc: lance.yang, baohua, akpm, ljs, riel, liam, vbabka, harry, jannh, qi.zheng, pfalcato, ayushr, linux-mm, linux-kernel On Thu, Nov 05, 2026 at 11:06:29AM +0100, David Hildenbrand (Arm) wrote: >On 11/5/26 10:00, Baolin Wang wrote: >> CC Barry, who added the TTU_SYNC in shrink_folio_list(). >> >> On 11/5/26 1:48 PM, Lance Yang wrote: >>> >>> >>> On 2026/11/5 12:54, Lance Yang wrote: >>>> >>>> >>>> Good catch! >>>> >>>> >>>> Right. Whether the folio can be PMD-mapped doesn't tell us whether its >>>> PTE table can be reclaimed, so those extra checks can skip synchronization >>>> that the caller explicitly asked for ;) >>> >>> Ouch ... one thing I missed ... what about order-0 reclaim? >>> >>> shrink_folio_list() only sets TTU_SYNC for large folio, so can't we still >>> hit the same race there? >> >> Thanks Lance. But this matches the purpose of the TTU_SYNC flag, which is used >> to avoid racy checks with PVMW_SYNC. If no TTU_SYNC flag is set, it means the >> caller can tolerate some racy checks. >> >> For this case, I think we should set TTU_SYNC unconditionally in >> shrink_folio_list(). Barry, what do you think? > >That makes sense. I hope this doesn't throw a big wrench into our reclaim >performance numbers. > >Can someone benchmark this? > >But maybe we should really follow-up with the folio-size based optimization in >sync_with_folio_pmd_zap(), where we only need a memory barrier (and sufficient >documentation why something that cannot be PMD-mapped can get away cheaper). Dug into the history a bit :D TTU_SYNC goes back to Hugh's THP splitting fix [1]. A racing zap could clear the PTE/PMD before updating mapcount, so the walker could skip the lock and return before the accounting caught up. Barry later enabled it for large-folio reclaim [2], to avoid skipping temporarily cleared PTEs and ending up with a partially unmapped folio. He mentioned that small folios had the same race, but with just one PTE, they couldn't be partially unmapped. That's where the large-folio check in shrink_folio_list() came from. Then PTE table reclaim [3] added another way to end up with an empty PMD, even for a folio that can't be PMD-mapped. The truncation fix [4] moved the rmap flush before the PMD clear and extended synchronization in zap_pmd_range() to cover those folios too. Looking back at the truncation discussion [5], you also pointed out that page_vma_mapped_walk() needed checking, and suggested a possible folio-size based optimization in the helper. [1] https://lore.kernel.org/all/c1e95853-8bcd-d8fd-55fa-e7f2488e78f@google.com/ [2] https://lore.kernel.org/all/20240306095219.71086-1-21cnbao@gmail.com/ [3] https://lore.kernel.org/all/92aba2b319a734913f18ba41e7d86a265f0b84e2.1733305182.git.zhengqi.arch@bytedance.com/ [4] https://lore.kernel.org/all/14ebbd556cc4941f0ebc43da59fa6a2fdca25dc8.1793148609.git.baolin.wang@linux.alibaba.com/ [5] https://lore.kernel.org/all/08a0907f-8e3a-4071-a2e4-fd1411c446f3@kernel.org/ So, how about starting with order-0 folios? ---8<--- diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 74cca0e3c726..d01fe8282417 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -872,6 +872,8 @@ struct page_vma_mapped_walk { spinlock_t *ptl; unsigned int flags; bool pgoff_is_anon : 1; + /* Set from the folio, not the number of pages being walked. */ + bool folio_is_order0 : 1; }; #define DEFINE_FOLIO_VMA_WALK(name, _folio, _vma, _address, _flags) \ @@ -883,6 +885,7 @@ struct page_vma_mapped_walk { .address = _address, \ .flags = _flags, \ .pgoff_is_anon = folio_test_anon(_folio), \ + .folio_is_order0 = !folio_test_large(_folio), \ } static inline void page_vma_mapped_walk_done(struct page_vma_mapped_walk *pvmw) diff --git a/mm/internal.h b/mm/internal.h index 9b8864a8540a..e70869b38292 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -592,21 +592,29 @@ void pmd_install(struct mm_struct *mm, pmd_t *pmd, pgtable_t *pte); * sync_with_folio_pmd_zap - sync with concurrent zapping of folios * @mm: The mm_struct. * @pmdp: Pointer to the pmd that was found to be pmd_none(). + * @order0: True if the target is known to be an order-0 folio. * - * When we find a pmd_none() while unmapping a folio without holding the PTL, - * zap_huge_pmd() may have cleared the PMD but not yet updated the folio - * mapcount, and zap_empty_pte_table() clears the PMD only after the rmaps - * for the table have been removed, but nothing orders the pmd_none() read - * against later mapcount reads. Skipping the PMD without synchronization - * could make folio unmapping code assume that unmapping failed. + * An empty PMD does not guarantee that a later mapcount read sees the + * completed rmap updates. For PMD-mapped folios, zap_huge_pmd() clears + * the PMD before updating mapcount, so we must wait for the PMD lock. * - * Wait for concurrent zapping to complete by grabbing the PTL. + * Order-0 folios cannot be PMD-mapped. Their fully ordered + * atomic_add_negative() updates mapcount before PTE table reclaim clears + * the PMD. The PTE lock preserves this ordering if another CPU reclaims + * the table. A read barrier therefore suffices for order-0 folios. + * Keep taking the PMD lock for all other folios. */ -static inline void sync_with_folio_pmd_zap(struct mm_struct *mm, pmd_t *pmdp) +static inline void sync_with_folio_pmd_zap(struct mm_struct *mm, pmd_t *pmdp, + bool order0) { - spinlock_t *ptl = pmd_lock(mm, pmdp); + if (order0) { + /* Order the PMD read before subsequent mapcount reads. */ + smp_rmb(); + } else { + spinlock_t *ptl = pmd_lock(mm, pmdp); - spin_unlock(ptl); + spin_unlock(ptl); + } } struct zap_details; diff --git a/mm/memory.c b/mm/memory.c index 44376321180b..8955d1cd31b0 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2112,7 +2112,8 @@ static inline unsigned long zap_pmd_range(struct mmu_gather *tlb, /* fall through */ } else if (pmd_none(*pmd)) { if (details && details->single_folio) - sync_with_folio_pmd_zap(tlb->mm, pmd); + sync_with_folio_pmd_zap(tlb->mm, pmd, + !folio_test_large(details->single_folio)); addr = next; continue; } diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c index e6b6170c2f4f..36881f8fd45e 100644 --- a/mm/page_vma_mapped.c +++ b/mm/page_vma_mapped.c @@ -293,11 +293,9 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) spin_unlock(pvmw->ptl); pvmw->ptl = NULL; } else if (!pmd_present(pmde)) { - if ((pvmw->flags & PVMW_SYNC) && - thp_vma_suitable_order(vma, pvmw->address, - PMD_ORDER) && - (pvmw->nr_pages >= HPAGE_PMD_NR)) - sync_with_folio_pmd_zap(mm, pvmw->pmd); + if (pvmw->flags & PVMW_SYNC) + sync_with_folio_pmd_zap(mm, pvmw->pmd, + pvmw->folio_is_order0); goto next_pmd; } @@ -379,6 +377,7 @@ unsigned long page_mapped_in_vma(const struct page *page, .nr_pages = 1, .vma = vma, .flags = PVMW_SYNC, + .folio_is_order0 = !folio_test_large(folio), }; if (folio_test_anon(folio)) diff --git a/mm/vmscan.c b/mm/vmscan.c index 7db6be53f3a9..79cfa9bc475b 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1434,26 +1434,17 @@ static unsigned int shrink_folio_list(struct list_head *folio_list, * processes. Try to unmap it here. */ if (folio_mapped(folio)) { - enum ttu_flags flags = TTU_BATCH_FLUSH; + enum ttu_flags flags = TTU_BATCH_FLUSH | TTU_SYNC; bool was_swapbacked = folio_test_swapbacked(folio); if (folio_test_pmd_mappable(folio)) flags |= TTU_SPLIT_HUGE_PMD; + /* - * Without TTU_SYNC, try_to_unmap will only begin to - * hold PTL from the first present PTE within a large - * folio. Some initial PTEs might be skipped due to - * races with parallel PTE writes in which PTEs can be - * cleared temporarily before being written new present - * values. This will lead to a large folio is still - * mapped while some subpages have been partially - * unmapped after try_to_unmap; TTU_SYNC helps - * try_to_unmap acquire PTL from the first PTE, - * eliminating the influence of temporary PTE values. + * Synchronize with concurrent rmap updates before checking + * folio_mapped(). Also avoid skipping temporarily cleared + * PTEs, which could leave a large folio partially mapped. */ - if (folio_test_large(folio)) - flags |= TTU_SYNC; - try_to_unmap(folio, flags); if (folio_mapped(folio)) { stat->nr_unmap_fail += nr_pages; -- atomic_add_negative() provides full ordering for mapcount updates. With 243cf1f67b36, rmap removal completes before clearing PMD, including when another CPU reclaims an empty table under PTE lock. So smp_rmb() is enough here, without an extra writer-side barrier. nr_pages == 1 isn't enough to identify an order-0 folio, since page_mapped_in_vma() also checks individual pages of large folios. We can then enable TTU_SYNC for order-0 reclaim and use smp_rmb() on an empty PMD, while keeping PMD locking for large folios. (Haven't benchmarked it yet) ... Cheers, Lance ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] mm: synchronize with PTE table reclaim for all folio sizes 2026-11-05 2:56 [PATCH] mm: synchronize with PTE table reclaim for all folio sizes Baolin Wang 2026-11-05 3:22 ` Qi Zheng 2026-11-05 4:54 ` Lance Yang @ 2026-11-05 11:37 ` Pedro Falcato 2 siblings, 0 replies; 9+ messages in thread From: Pedro Falcato @ 2026-11-05 11:37 UTC (permalink / raw) To: Baolin Wang Cc: akpm, david, ljs, riel, liam, vbabka, harry, jannh, lance.yang, qi.zheng, ayushr, linux-mm, linux-kernel On Thu, Nov 05, 2026 at 10:56:28AM +0800, Baolin Wang wrote: > Similar to commit 243cf1f67b36 ("mm: memory: fix truncation removing mapped > folios"), the '!pmd_present()' check in page_vma_mapped_walk() can also race > with concurrent page table reclamation via zap_empty_pte_table(). > > page_vma_mapped_walk() is used by many functions including try_to_unmap(), > try_to_migrate(), folio_mkclean(), pfn_mkclean_range(), remove_migration_ptes(), > page_mapped_in_vma(), and mapping_wrprotect_range(). Except for try_to_unmap() > and try_to_migrate(), the other callers do not perform post-walk accounting > checks, so the race has no observable impact on them. > > For migration, the race can cause unnecessary retries. For reclaim via > try_to_unmap(), the race can cause failure to reclaim a folio that is in > fact already unmapped (as shown below), which may lead to unexpected OOM > or increased refaults. This is very handwavy. Do you have numbers? Does this actually make sense? -- Pedro ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-11-05 13:23 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-05 2:56 [PATCH] mm: synchronize with PTE table reclaim for all folio sizes Baolin Wang 2026-11-05 3:22 ` Qi Zheng 2026-11-05 4:54 ` Lance Yang 2026-11-05 5:48 ` Lance Yang 2026-11-05 9:00 ` Baolin Wang 2026-11-05 10:06 ` David Hildenbrand (Arm) 2026-11-05 11:18 ` Baolin Wang 2026-11-05 13:22 ` Lance Yang 2026-11-05 11:37 ` Pedro Falcato
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®