[PATCH v3 13/15] mm/shmem: fix truncate loop after boundary swapin failure
From: David Hildenbrand (Arm)
Date: Wed Oct 28 2026 - 06:02:12 EST
If swapping in a large entry at the new EOF fails, shmem_undo_range()
leaves the entry in place. Its final scan skips the entry, then restarts
from the same start index and finds it again. Truncation can loop forever
while holding i_rwsem.
Advance start past the skipped entry so that a restarted scan can finish,
as we already do for folios that cannot be split.
Note that this does not fix the missing zeroing when boundary swapin fails,
it merely prevents the endless loop.
Fixes: 8a1968bd997f ("mm/shmem, swap: fix race of truncate and swap entry split")
Cc: stable@xxxxxxxxxxxxxxx
Acked-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
Tested-by: Gregory Price (Meta) <gourry@xxxxxxxxxx>
Reviewed-by: Baolin Wang <baolin.wang@xxxxxxxxxxxxxxxxx>
Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
---
mm/shmem.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index da2f5cd37eee..63f7fccf2ff5 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1434,14 +1434,16 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
order = shmem_confirm_swap(mapping, indices[i],
radix_to_swp_entry(folio));
/*
- * If found a large swap entry cross the end or start
- * border, skip it as the truncate_inode_partial_folio
- * above should have at least zerod its content once.
+ * Skip boundary entries when restarting
+ * the scan to avoid endless loops after
+ * swapin failure.
*/
if (order > 0) {
base = round_down(base, 1 << order);
- if (base < start || base + (1 << order) > end)
+ if (base < start || base + (1 << order) > end) {
+ start = max(start, base + (1 << order));
continue;
+ }
}
/* Swap was replaced by page or extended, retry */
index = base;
--
2.43.0