All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chris Mason <mason@kernel.org>
To: mason@kernel.org, tytso@mit.edu, cem@kernel.org,
	david@fromorbit.com, jack@suse.cz, djwong@kernel.org,
	josef@toxicpanda.com, hch@lst.de, linux-xfs@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org,
	tz2294@columbia.edu, brauner@kernel.org
Subject: [PATCH v9 04/14] iomap: don't punch delalloc under a dirty EOF block
Date: Thu, 12 Nov 2026 16:46:08 -0500	[thread overview]
Message-ID: <20261112214604.67875-5-mason@kernel.org> (raw)
In-Reply-To: <20261112214604.67875-1-mason@kernel.org>

After a failed buffered write, writeback on XFS can write in place to
the shared block that contains EOF, and corrupt the other file that
shares it.

After a failed buffered write, iomap_write_delalloc_release() punches
the delalloc reservation of the write, except under dirty folios, which
still need it. It looks for dirty folios only up to i_size, and punches
the rest unconditionally. But XFS rounds that punch down to a block
boundary, so the block that contains EOF loses its COW fork reservation,
and writeback goes to the shared block:

    block size 4096, i_size 5000, reservation 4096 to 8192
    searched for dirty folios:  4096 to 5000
    punched unconditionally:    5000 to 8192
    rounded to blocks by XFS:   4096 to 8192

The fix used here is to look for dirty folios up to the end of the block
that contains EOF.

Fixes: f43dc4dc3eff ("iomap: buffered write failure should not truncate the page cache")
Fixes: f6f91d290c8b ("xfs: punch delalloc extents from the COW fork for COW writes")
Signed-off-by: Chris Mason <mason@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---

Notes:
    v6->v9: no change

 fs/iomap/buffered-io.c | 34 ++++++++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index d6e2f1ded5ca..d0980a7f27d1 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -1495,9 +1495,34 @@ static void iomap_write_delalloc_scan(struct inode *inode,
  * mapping_seek_hole_data() will return it, and we will skip the punch because
  * the folio is dirty. THis is incorrect - we always need to punch out delalloc
  * beyond EOF in this case as writeback will never write back and covert that
- * delalloc block beyond EOF. Hence we limit the cached data scan range to EOF,
- * resulting in always punching out the range from the EOF to the end of the
- * range the iomap spans.
+ * delalloc block beyond EOF. Hence we limit the cached data scan range, and
+ * always punch out the range from the end of the scan to the end of the range
+ * the iomap spans.
+ *
+ * The scan cannot simply end at EOF, because EOF is a byte offset and a
+ * delalloc reservation covers whole blocks. If EOF lies inside a block, it
+ * splits that block in two:
+ *
+ *	[block start, EOF)	File data. If it is dirty it will be written
+ *				back, so the block must keep its delalloc
+ *				backing.
+ *	[EOF, block end)	Beyond EOF. By the rule above, this would always
+ *				be punched out.
+ *
+ * Both parts share one reservation, and it cannot be split: a punch of a part
+ * of a block takes away the reservation for the whole block. If the scan ended
+ * at EOF, we would skip the punch for the first part because it is dirty, and
+ * then punch the second part, which takes the reservation away from under the
+ * dirty data in the first part. On XFS, writeback that has lost a COW fork
+ * reservation like this writes in place, over a block that is shared with
+ * another file.
+ *
+ * Hence we treat the EOF block as a whole, and let the cached data over it
+ * decide: the scan range is rounded up to the end of the block that EOF lies
+ * in. If the data over the EOF block is dirty, the scan moves the punch start
+ * past the block. If it is clean, the block is punched like any other block in
+ * the range. Only blocks that start at or beyond EOF are punched
+ * unconditionally. If EOF is block aligned, the round up changes nothing.
  *
  * Intervals are of the form [start_byte, end_byte) (i.e. open ended) because it
  * matches the intervals returned by mapping_seek_hole_data(). i.e. SEEK_DATA
@@ -1511,7 +1536,8 @@ void iomap_write_delalloc_release(struct inode *inode, loff_t start_byte,
 		iomap_punch_t punch)
 {
 	loff_t punch_start_byte = start_byte;
-	loff_t scan_end_byte = min(i_size_read(inode), end_byte);
+	loff_t scan_end_byte = min_t(u64, end_byte,
+			round_up((u64)i_size_read(inode), i_blocksize(inode)));
 
 	/*
 	 * The caller must hold invalidate_lock to avoid races with page faults
-- 
2.55.0


  parent reply	other threads:[~2026-11-12 21:46 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 21:46 [PATCH v9 00/14] reflink and atomic write bugs Chris Mason
2026-11-12 21:46 ` [PATCH v9 01/14] xfs: don't skip the flush before shifting extents Chris Mason
2026-11-12 21:55   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 02/14] xfs: flush the shifted range before changing the file Chris Mason
2026-11-12 21:54   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 03/14] xfs: don't change the block map under a dirty folio Chris Mason
2026-11-12 22:03   ` sashiko-bot
2026-11-12 21:46 ` Chris Mason [this message]
2026-11-12 21:54   ` [PATCH v9 04/14] iomap: don't punch delalloc under a dirty EOF block sashiko-bot
2026-11-12 21:46 ` [PATCH v9 05/14] fs: validate atomic writes after O_APPEND and limits Chris Mason
2026-11-12 22:16   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 06/14] xfs: recheck direct write alignment after write checks Chris Mason
2026-11-12 22:22   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 07/14] xfs: test for reflink after the dio " Chris Mason
2026-11-12 21:54   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 08/14] xfs: refuse writes to files racing with realtime inode flag changes Chris Mason
2026-11-12 22:11   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 09/14] xfs: test for always COW after the dio write checks Chris Mason
2026-11-12 21:53   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 10/14] xfs: refuse fallocate racing with realtime flag changes Chris Mason
2026-11-12 22:11   ` sashiko-bot
2026-11-12 21:46 ` [PATCH v9 11/14] xfs: take the MMAPLOCK in xfs_fileattr_set() Chris Mason
2026-11-12 22:13   ` sashiko-bot
2026-11-12 23:49   ` Christoph Hellwig
2026-11-12 21:46 ` [PATCH v9 12/14] xfs: restart faults racing with realtime flag changes Chris Mason
2026-11-12 22:08   ` sashiko-bot
2026-11-12 23:50   ` Christoph Hellwig
2026-11-12 21:46 ` [PATCH v9 13/14] xfs: don't enable DAX on zoned inodes Chris Mason
2026-11-12 22:03   ` sashiko-bot
2026-11-12 23:50   ` Christoph Hellwig
2026-11-12 21:46 ` [PATCH v9 14/14] xfs: test for realtime and reflink under the pNFS locks Chris Mason
2026-11-12 22:02   ` sashiko-bot
2026-11-12 23:51   ` Christoph Hellwig

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261112214604.67875-5-mason@kernel.org \
    --to=mason@kernel.org \
    --cc=brauner@kernel.org \
    --cc=cem@kernel.org \
    --cc=david@fromorbit.com \
    --cc=djwong@kernel.org \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=josef@toxicpanda.com \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    --cc=tytso@mit.edu \
    --cc=tz2294@columbia.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.