From: Brian Foster <bfoster@redhat.com>
To: linux-fsdevel@vger.kernel.org, linux-xfs@vger.kernel.org
Subject: [PATCH v3 3/8] iomap, xfs: lift zero range hole mapping flush into xfs
Date: Mon, 9 Mar 2026 09:45:01 -0400 [thread overview]
Message-ID: <20260309134506.167663-4-bfoster@redhat.com> (raw)
In-Reply-To: <20260309134506.167663-1-bfoster@redhat.com>
iomap zero range has a wart in that it also flushes dirty pagecache
over hole mappings (rather than only unwritten mappings). This was
included to accommodate a quirk in XFS where COW fork preallocation
can exist over a hole in the data fork, and the associated range is
reported as a hole. This is because the range actually is a hole,
but XFS also has an optimization where if COW fork blocks exist for
a range being written to, those blocks are used regardless of
whether the data fork blocks are shared or not. For zeroing, COW
fork blocks over a data fork hole are only relevant if the range is
dirty in pagecache, otherwise the range is already considered
zeroed.
The easiest way to deal with this corner case is to flush the
pagecache to trigger COW remapping into the data fork, and then
operate on the updated on-disk state. The problem is that ext4
cannot accommodate a flush from this context due to being a
transaction deadlock vector.
Outside of the hole quirk, ext4 can avoid the flush for zero range
by using the recently introduced folio batch lookup mechanism for
unwritten mappings. Therefore, take the next logical step and lift
the hole handling logic into the XFS iomap_begin handler. iomap will
still flush on unwritten mappings without a folio batch, and XFS
will flush and retry mapping lookups in the case where it would
otherwise report a hole with dirty pagecache during a zero range.
Note that this is intended to be a fairly straightforward lift and
otherwise not change behavior. Now that the flush exists within XFS,
follow on patches can further optimize it.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/iomap/buffered-io.c | 2 +-
fs/xfs/xfs_iomap.c | 25 ++++++++++++++++++++++---
2 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index bc82083e420a..0999aca6e5cc 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -1642,7 +1642,7 @@ iomap_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero,
srcmap->type == IOMAP_UNWRITTEN)) {
s64 status;
- if (range_dirty) {
+ if (range_dirty && srcmap->type == IOMAP_UNWRITTEN) {
range_dirty = false;
status = iomap_zero_iter_flush_and_stale(&iter);
} else {
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 0e323e4e304b..966fb9d8b9df 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -1811,6 +1811,7 @@ xfs_buffered_write_iomap_begin(
if (error)
return error;
+restart:
error = xfs_ilock_for_iomap(ip, flags, &lockmode);
if (error)
return error;
@@ -1838,9 +1839,27 @@ xfs_buffered_write_iomap_begin(
if (eof)
imap.br_startoff = end_fsb; /* fake hole until the end */
- /* We never need to allocate blocks for zeroing or unsharing a hole. */
- if ((flags & (IOMAP_UNSHARE | IOMAP_ZERO)) &&
- imap.br_startoff > offset_fsb) {
+ /* We never need to allocate blocks for unsharing a hole. */
+ if ((flags & IOMAP_UNSHARE) && imap.br_startoff > offset_fsb) {
+ xfs_hole_to_iomap(ip, iomap, offset_fsb, imap.br_startoff);
+ goto out_unlock;
+ }
+
+ /*
+ * We may need to zero over a hole in the data fork if it's fronted by
+ * COW blocks and dirty pagecache. To make sure zeroing occurs, force
+ * writeback to remap pending blocks and restart the lookup.
+ */
+ if ((flags & IOMAP_ZERO) && imap.br_startoff > offset_fsb) {
+ if (filemap_range_needs_writeback(inode->i_mapping, offset,
+ offset + count - 1)) {
+ xfs_iunlock(ip, lockmode);
+ error = filemap_write_and_wait_range(inode->i_mapping,
+ offset, offset + count - 1);
+ if (error)
+ return error;
+ goto restart;
+ }
xfs_hole_to_iomap(ip, iomap, offset_fsb, imap.br_startoff);
goto out_unlock;
}
--
2.52.0
next prev parent reply other threads:[~2026-03-09 13:45 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-09 13:44 [PATCH v3 0/8] iomap, xfs: improve zero range flushing and lookup Brian Foster
2026-03-09 13:44 ` [PATCH v3 1/8] xfs: fix iomap hole map reporting for zoned zero range Brian Foster
2026-03-09 17:11 ` Darrick J. Wong
2026-03-09 18:18 ` Brian Foster
2026-03-10 14:47 ` Darrick J. Wong
2026-03-10 6:45 ` Christoph Hellwig
2026-03-09 13:45 ` [PATCH v3 2/8] xfs: flush dirty pagecache over hole in zoned mode " Brian Foster
2026-03-09 17:22 ` Darrick J. Wong
2026-03-09 18:19 ` Brian Foster
2026-03-10 6:47 ` Christoph Hellwig
2026-03-10 14:48 ` Darrick J. Wong
2026-03-10 6:45 ` Christoph Hellwig
2026-03-09 13:45 ` Brian Foster [this message]
2026-03-09 17:40 ` [PATCH v3 3/8] iomap, xfs: lift zero range hole mapping flush into xfs Darrick J. Wong
2026-03-10 6:47 ` Christoph Hellwig
2026-03-09 13:45 ` [PATCH v3 4/8] xfs: flush eof folio before insert range size update Brian Foster
2026-03-09 17:32 ` Darrick J. Wong
2026-03-09 18:24 ` Brian Foster
2026-03-09 13:45 ` [PATCH v3 5/8] xfs: look up cow fork extent earlier for buffered iomap_begin Brian Foster
2026-03-09 13:45 ` [PATCH v3 6/8] xfs: only flush when COW fork blocks overlap data fork holes Brian Foster
2026-03-09 17:47 ` Darrick J. Wong
2026-03-09 13:45 ` [PATCH v3 7/8] xfs: replace zero range flush with folio batch Brian Foster
2026-03-09 17:48 ` Darrick J. Wong
2026-03-09 13:45 ` [PATCH v3 8/8] xfs: report cow mappings with dirty pagecache for iomap zero range Brian Foster
2026-03-09 17:56 ` Darrick J. Wong
2026-03-09 18:31 ` Brian Foster
2026-03-09 18:38 ` Darrick J. Wong
2026-03-10 6:50 ` Christoph Hellwig
2026-03-10 14:52 ` Darrick J. Wong
2026-03-10 14:59 ` Christoph Hellwig
2026-03-10 6:49 ` Christoph Hellwig
2026-03-10 6:45 ` [PATCH v3 0/8] iomap, xfs: improve zero range flushing and lookup 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=20260309134506.167663-4-bfoster@redhat.com \
--to=bfoster@redhat.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox