[RESEND v7 26/29] mm: don't PTE-batch a swap-in over a hardware-poisoned subpage

From: Usama Arif

Date: Mon Sep 14 2026 - 09:28:04 EST


do_swap_page() checks PageHWPoison() on the faulting page, but the
large-folio batching path then maps the rest of the folio without looking
at the other subpages. A poisoned subpage that is not the one being faulted
gets mapped anyway, and the fault that would have reported it never
happens.

Skip batching for such a folio and let each page take its own fault, where
the existing check catches it. Test every subpage rather than the
folio-level PG_has_hwpoisoned: memory_failure() sets PageHWPoison() on the
subpage before it acquires the folio lock, which we hold here.

Signed-off-by: Usama Arif <usama.arif@xxxxxxxxx>
---
mm/memory.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/mm/memory.c b/mm/memory.c
index 84e1e1c22bffa..aa1f67b378587 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -5020,7 +5020,14 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
page_idx = 0;
address = vmf->address;
ptep = vmf->pte;
- if (folio_test_large(folio) && folio_test_swapcache(folio)) {
+ /*
+ * Scan every subpage rather than testing the folio-level
+ * PG_has_hwpoisoned: memory_failure() sets PageHWPoison on the subpage
+ * before it takes the folio lock, and we hold that lock, so the
+ * folio-level flag can still be clear here.
+ */
+ if (folio_test_large(folio) && folio_test_swapcache(folio) &&
+ !folio_has_hwpoisoned_subpage(folio)) {
int nr = folio_nr_pages(folio);
unsigned long idx = folio_page_idx(folio, page);
unsigned long folio_start = address - idx * PAGE_SIZE;
--
2.53.0-Meta