From a04e24028c37176b19256dba0507961d7ed5f1d6 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Wed, 8 Jul 2026 12:12:36 +0800 Subject: [PATCH] mm/vmscan: flush deferred TLB before freeing large folios ANBZ: #41994 commit 9af37d8be0fb61468ad5eb988b9875ec2ebdba8d stable. In reclaim, shrink_folio_list() unmaps PTEs with a deferred, batched TLB flush. The batch is only flushed by try_to_unmap_flush() near the end of the function, just before the order-0 folios collected in @free_folios are handed back to the allocator. Large folios don't go through @free_folios -- they're freed inline at the free_it label via destroy_large_folio(), which runs before that flush. So a large folio's pages can be returned to the buddy allocator and reused while another CPU still holds a stale TLB entry for them, and that CPU then reads or executes through the stale translation into the reused page. For file-backed large folios (e.g. executable text) this shows up as random SIGSEGV/SIGILL in user space, with fault addresses that don't match the code being run. Flush the deferred batch before freeing a large folio inline, the same way the order-0 path already waits for the flush. Upstream this is fixed as a side effect of commit bc2ff4cbc329 ("mm: free folios in a batch in shrink_folio_list()"), which is a larger change; this is the minimal fix for -stable. [backport-note] PatchPilot-Conflict-Type: context_drift, rename_refactor PatchPilot-Conflict-Files: mm/vmscan.c PatchPilot-Resolution: mm/vmscan.c: Keep folio_batch refactor; add try_to_unmap_flush before batching large folio Reported-by: Yingfu Zhou Fixes: bd4c82c22c36 ("mm, THP, swap: delay splitting THP after swapped out") Cc: Jiayuan Chen Signed-off-by: Jiayuan Chen Reviewed-by: Matthew Wilcox Signed-off-by: Sasha Levin Assisted-by: PatchPilot Signed-off-by: Dust Li --- mm/vmscan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/vmscan.c b/mm/vmscan.c index d9d9b44b1058..a204969e8a9c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1543,6 +1543,9 @@ static unsigned int shrink_folio_list(struct list_head *folio_list, nr_reclaimed += nr_pages; folio_unqueue_deferred_split(folio); + /* Deferred TLB entries must be flushed before a large folio is freed */ + if (unlikely(folio_test_large(folio))) + try_to_unmap_flush(); if (folio_batch_add(&free_folios, folio) == 0) { mem_cgroup_uncharge_folios(&free_folios); try_to_unmap_flush(); -- Gitee