[PATCH 13/46] mm/huge_memory: fix truncate racing with PMD zapping
David Hildenbrand (Arm)
david at kernel.org
Mon Oct 26 16:17:33 PDT 2026
From: Chris Mason <mason at kernel.org>
zap_huge_pmd_folio() removes the rmap before calling folio_mark_dirty().
If this was the last mapping, truncation can skip unmapping the folio
and proceed without waiting for the PMD lock, racing with dirtying.
ifs_set_range_dirty() can oops if MADV_DONTNEED races with
ftruncate() on XFS, when we get the folio set dirty after it was found
to not be mapped anymore.
Move folio_mark_dirty() and folio_mark_accessed() before rmap removal,
matching the PTE zap path. This keeps the folio mapped while updating
its state, so truncation must wait for the page table lock.
Fix generated by an LLM and sent by Chris Mason in private.
Fixes: fe6cf984939d ("mm/huge_memory: transfer the pmd dirty bit to the folio on zap")
Cc: stable at vger.kernel.org
Signed-off-by: Chris Mason <mason at kernel.org>
[ david: simplify comment and description ]
Signed-off-by: David Hildenbrand (Arm) <david at kernel.org>
---
mm/huge_memory.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2b31a425c7a32..eda020dd6e33f 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2495,6 +2495,14 @@ static void zap_huge_pmd_folio(struct mm_struct *mm, struct vm_area_struct *vma,
{
const bool is_device_private = folio_is_device_private(folio);
+ /* Transfer young+dirty while the folio is still mapped. */
+ if (is_present && !folio_test_anon(folio)) {
+ if (pmd_dirty(pmdval))
+ folio_mark_dirty(folio);
+ if (pmd_young(pmdval) && likely(vma_has_recency(vma)))
+ folio_mark_accessed(folio);
+ }
+
/* Present and device private folios are rmappable. */
if (is_present || is_device_private)
folio_remove_rmap_pmd(folio, &folio->page, vma);
@@ -2504,12 +2512,6 @@ static void zap_huge_pmd_folio(struct mm_struct *mm, struct vm_area_struct *vma,
} else {
add_mm_counter(mm, mm_counter_file(folio),
-HPAGE_PMD_NR);
-
- if (is_present && pmd_dirty(pmdval))
- folio_mark_dirty(folio);
- if (is_present && pmd_young(pmdval) &&
- likely(vma_has_recency(vma)))
- folio_mark_accessed(folio);
}
/* Device private folios are pinned. */
--
2.43.0
More information about the kexec
mailing list