[PATCH 1/3] mm/zswap: drop the stale zswap entry of a zero folio
From: Kairui Song via B4 Relay
Date: Thu Oct 29 2026 - 14:33:20 EST
From: Josef Bacik <josef@xxxxxxxxxxxxxx>
A folio can stay in the swap cache after zswap_store() has stored it,
when a swapin fault gets a reference on it before reclaim can remove it.
do_swap_page() maps an exclusive folio writable and, for a read fault,
keeps it in the swap cache, so it can be modified and swapped out again
to the same slot. zswap_store() handles this, it replaces the old entry,
or drops it if the store fails or zswap got disabled, "otherwise,
writeback could overwrite the new data in the swapfile".
swap_writeout() does not get as far as zswap_store() if the folio is
zero-filled now. It only sets the zero flag of the slot, and the zswap
entry with the previous contents stays in the tree and on the LRU:
reclaim fault zswap shrinker
zswap_store(D1)
swap cache hit,
map, write zeroes
swap_writeout()
zero flag set
folio reclaimed
zswap_writeback_entry()
swap_cache_alloc_folio()
decompress D1, uptodate
swap cache hit:
maps D1
Swapin looks at the zero flag first, but only when it has to read the
folio. The folio that zswap writeback leaves in the swap cache is up to
date as far as anybody can tell, so the process gets its old data back
instead of the zeroes it has written, until that folio is reclaimed.
Invalidate the zswap entries in the zero-filled case as well. The folio
is locked and in the swap cache, which keeps zswap writeback away.
Fixes: 0ca0c24e3211 ("mm: store zero pages to be swapped out in a bitmap")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
[kasong@xxxxxxxxxxx: fix zswap_invalidate() arguments]
Signed-off-by: Kairui Song <kasong@xxxxxxxxxxx>
---
mm/page_io.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/mm/page_io.c b/mm/page_io.c
index c6824fcd483e..65681eed9c80 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -224,7 +224,17 @@ int swap_writeout(struct swap_io_ctx *ctx, struct folio *folio)
* acquired internally by swap_zeromap_folio_set/clear.
*/
if (is_folio_zero_filled(folio)) {
+ swp_entry_t entry = folio->swap;
+
swap_zeromap_folio_set(folio);
+
+ /*
+ * The folio may have been stored in zswap before and dirtied
+ * again while it stayed in the swap cache. Drop the old copy,
+ * or zswap writeback would bring it back into the swap cache.
+ */
+ zswap_invalidate(swp_type(entry), swp_offset(entry),
+ folio_nr_pages(folio));
goto out_unlock;
}
--
2.56.0