[PATCH 07/16] mm/shmem: avoid moving the truncation start backwards

From: David Hildenbrand (Arm)

Date: Mon Oct 26 2026 - 05:47:31 EST


From: Chris Mason <mason@xxxxxxxxxx>

A nonuniform folio split can fail with -ENOMEM after splitting off some
smaller folios. shmem_undo_range() then sets start to the end of the
original folio, which may now lie before the truncation boundary.

The final scan can consequently discard data below the new EOF, or
keep retrying a large folio whose split point lies outside it.

Keep start at least at its original value when skipping a folio after
a failed split. The part in the truncated range has already been zeroed.

Fix generated by an LLM and sent by Chris Mason in private.

Fixes: 7460b470a131f ("mm/truncate: use folio_split() in truncate operation")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Chris Mason <mason@xxxxxxxxxx>
[ david: split fixes, simplify descriptions, adjust comments ]
Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
---
mm/shmem.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/mm/shmem.c b/mm/shmem.c
index c551e33c697fe..2410ec46ee1d1 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1388,7 +1388,13 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
same_folio = lend < folio_next_pos(folio);
folio_mark_dirty(folio);
if (!truncate_inode_partial_folio(folio, lstart, lend)) {
- start = folio_next_index(folio);
+ /*
+ * Splitting failed, but the part in the range is
+ * already zeroed, so skip this folio. The failed split
+ * may have shrunk it; do not move start backwards and
+ * discard data before lstart.
+ */
+ start = max(start, folio_next_index(folio));
if (same_folio)
end = folio->index;
}

--
2.43.0