* + mm-truncate-align-truncation-boundaries-to-mapping-minimum-folio-order.patch added to mm-new branch
@ 2026-09-28 21:48 Andrew Morton
0 siblings, 0 replies; only message in thread
From: Andrew Morton @ 2026-09-28 21:48 UTC (permalink / raw)
To: mm-commits, ziy, yukuai, yizhang089, yangerkun, willy,
wangkefeng.wang, vbabka, surenb, rppt, mhocko, ljs, liam,
joannelkoong, jack, hughd, djwong, david, chengzhihao1, bfoster,
baolin.wang, yi.zhang, akpm
The patch titled
Subject: mm/truncate: align truncation boundaries to mapping minimum folio order
has been added to the -mm mm-new branch. Its filename is
mm-truncate-align-truncation-boundaries-to-mapping-minimum-folio-order.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/mm-truncate-align-truncation-boundaries-to-mapping-minimum-folio-order.patch
This patch will later appear in the mm-new branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Note, mm-new is a provisional staging ground for work-in-progress
patches, and acceptance into mm-new is a notification for others take
notice and to finish up reviews. Please do not hesitate to respond to
review feedback and post updated versions to replace or incrementally
fixup patches in mm-new.
The mm-new branch of mm.git is not included in linux-next
If a few days of testing in mm-new is successful, the patch will me moved
into mm.git's mm-unstable branch, which is included in linux-next
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: Zhang Yi <yi.zhang@huawei.com>
Subject: mm/truncate: align truncation boundaries to mapping minimum folio order
Date: Mon, 28 Sep 2026 20:08:30 +0800
Patch series "mm/truncate: fix data loss when truncating straddling large
folios", v5.
This is the fifth version fixing data loss when truncating straddling
large folios caught on the upcomming ext4 + iomap buffered I/O conversion.
When truncate_inode_pages_range() punches a hole or truncates a file,
truncate_inode_partial_folio() splits a large folio so that the caller can
drop the in-range sub-folios while keeping the out-of-range tail intact.
This series fixes three distinct problems in that path that can each lose
the valid out-of-range tail of a straddling folio, plus a follow-up that
clarifies the return value semantics.
Patch 01 aligns the truncation boundaries inwards to the mapping minimum
folio order in truncate_inode_pages_range(). With a non-zero min_order,
folio_split() stops at min_order instead of order 0, so a boundary
computed at page granularity can land inside a min-order-aligned sub-folio
and the truncate loop drops that whole chunk, valid tail included, causing
data loss.
Patch 02 looks the end-edge straddler up by its page index through
__filemap_get_folio() in truncate_inode_partial_folio(). After the first
split the straddler is unlocked and only transiently ref'd in the page
cache, so the page pointer derived from the original folio can be freed
and reallocated as a different folio in the same mapping, and the mapping
check cannot catch it, which may cause incorrect splitting and potential
data loss.
Patch 03 reworks the contract between truncate_inode_partial_folio() and
its callers. If the second split of the straddler fails, the function
reported success unconditionally, and the leftover incorrect end position
could cause the truncate loop to drop that valid tail. After rework, it
tells the caller the exact page range safe to discard via new pstart/pend
out-parameters, so the truncate loop never touches a straddling folio that
still holds valid out-of-range data.
Patch 04 clarifies the return value semantics to "at least one split
succeeded", which is all the shmem caller needs to decide whether to reset
its scan loop.
The second patch fixes a pre-existing race issue that is reachable today,
so it is Cc'd to stable. Patches 01 and 03 require a dirty large folio
that carries no filesystem private data, so they are not reachable on
current filesystems. They were found while developing the upcoming ext4
iomap buffered I/O path. [1]
In addition, another two pre-existing issues were found during the
development of this series:
- On 32-bit systems with a non-zero min_order filesystem, truncating or
any operation operation involving folio_next_index() on the folio
containing MAX_LFS_FILESIZE can cause the index calculation to
overflow, posing an unpredictable risk. [2]
- In shmem, at the last call site of truncate_inode_partial_folio() in
shmem_undo_range(), the restart-loop check is wrong, which leaves
split sub-folios behind. [3]
These two issues need to be fixed separately.
When the mapping has a non-zero minimum folio order (min_order),
folio_split() in truncate_inode_partial_folio() stops at min_order instead
of order 0, so the sub-folio containing a split point stays aligned to 1
<< min_order rather than to a single page. The original boundaries in
truncate_inode_pages_range() were based on page granularity, so either
boundary could land inside the min_order chunk at its edge, and the
truncation loop would drop that whole chunk, valid out-of-range tail
included.
For example, a 64K (order-4) folio with min_order = 2 (16K) punched from
offset 0 to 36K:
split @p0 -> [p0-p3, p4-p7, p8-p15] # non-uniform, min_order
folio2 = p8-p15 # straddles: p8 in range, p9-p15 tail valid
2nd split of folio2 -> [p8-p11, p12-p15] # success
end(old) = p9 # BUG: p9 inside [p8-p11]
loop truncates ... p8-p11 # p9-p11's valid tail is lost
It has gone unnoticed so far for two reasons. A non-zero min_order is
only used by filesystems with a block or sector size larger than the page
size, and those either always write back the affected range before
punching a hole or truncating, or they carry filesystem private data on
dirty folios (e.g. buffer_head), which makes filemap_release_folio() fail
and folio_split() abort with -EBUSY, so the folio is never split and the
old start/end boundaries remain valid. The bug only becomes reachable on
paths that truncate dirty large folios without prior writeback and without
filesystem private data, such as the upcoming ext4 iomap buffered I/O
path.
Align both start (rounded up) and end (rounded down) to the mapping
minimum folio order so they always fall on a folio boundary.
Link: https://lore.kernel.org/20260928120833.3440834-2-yi.zhang@huaweicloud.com
Link: https://lore.kernel.org/linux-fsdevel/a638a8fb-c184-4069-ae33-379ec12cd514@huaweicloud.com/ [1]
Link: https://lore.kernel.org/linux-mm/5a454f2a-8ae2-491d-b903-750c945cfb9d@huaweicloud.com/ [2]
Link: https://lore.kernel.org/linux-mm/5pthbyxtn7q6xi4fmkofvksmcjzfnujcw2g4fxmxjzfin5pbgf@zui3vcimb4cv/ [3]
Fixes: e220917fa5077 ("mm: split a folio in minimum folio order chunks")
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reported-by: Joanne Koong <joannelkoong@gmail.com>
Closes: https://lore.kernel.org/linux-mm/CAJnrk1bQYUe6+1ryyJur5EEnZYrC+_5AYsy=OWzVRgD4202y1g@mail.gmail.com/
Suggested-by: Zi Yan <ziy@nvidia.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Zi Yan <ziy@nvidia.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Darrick J. Wong <djwong@kernel.org>
Cc: <yizhang089@gmail.com>
Cc: Yang Erkun <yangerkun@huawei.com>
Cc: Zhihao Cheng <chengzhihao1@huawei.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: <yukuai@fnnas.com>
---
mm/truncate.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
--- a/mm/truncate.c~mm-truncate-align-truncation-boundaries-to-mapping-minimum-folio-order
+++ a/mm/truncate.c
@@ -345,9 +345,11 @@ long mapping_evict_folio(struct address_
* @lstart: offset from which to truncate
* @lend: offset to which to truncate (inclusive)
*
- * Truncate the page cache, removing the pages that are between
- * specified offsets (and zeroing out partial pages
- * if lstart or lend + 1 is not page aligned).
+ * Truncate the page cache, removing the folios that are between specified
+ * offsets (and zeroing out partial folios if lstart or lend + 1 is not
+ * folio aligned). For mappings with a non-zero minimum folio order, the
+ * boundaries are aligned inwards to 1 << min_order so the edge sub-folio
+ * straddling the range is kept.
*
* Truncate takes two passes - the first pass is nonblocking. It will not
* block on page locks and it will not block on writeback. The second pass
@@ -366,6 +368,7 @@ long mapping_evict_folio(struct address_
void truncate_inode_pages_range(struct address_space *mapping,
loff_t lstart, uoff_t lend)
{
+ pgoff_t min_nrpages = mapping_min_folio_nrpages(mapping);
pgoff_t start; /* inclusive */
pgoff_t end; /* exclusive */
struct folio_batch fbatch;
@@ -379,9 +382,8 @@ void truncate_inode_pages_range(struct a
return;
/*
- * 'start' and 'end' always covers the range of pages to be fully
- * truncated. Partial pages are covered with 'partial_start' at the
- * start of the range and 'partial_end' at the end of the range.
+ * 'start' and 'end' always covers the range of folios to be fully
+ * truncated, with both boundaries aligned inwards to 1 << min_order.
* Note that 'end' is exclusive while 'lend' is inclusive.
*/
start = (lstart + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -395,6 +397,10 @@ void truncate_inode_pages_range(struct a
else
end = (lend + 1) >> PAGE_SHIFT;
+ start = round_up(start, min_nrpages);
+ if (end != (pgoff_t)-1)
+ end = round_down(end, min_nrpages);
+
folio_batch_init(&fbatch);
index = start;
while (index < end && find_lock_entries(mapping, &index, end - 1,
_
Patches currently in -mm which might be from yi.zhang@huawei.com are
mm-truncate-align-truncation-boundaries-to-mapping-minimum-folio-order.patch
mm-truncate-look-up-the-end-edge-straddler-by-index.patch
mm-truncate-fix-data-loss-when-splitting-straddling-large-folios-fails.patch
mm-truncate-clarify-return-value-of-truncate_inode_partial_folio.patch
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-28 21:48 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28 21:48 + mm-truncate-align-truncation-boundaries-to-mapping-minimum-folio-order.patch added to mm-new branch Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox