All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dave Chinner <dgc@kernel.org>
To: linux-xfs@vger.kernel.org
Cc: cem@kernel.org
Subject: [PATCH 04/33] xfs: factor out COW iomap handling from xfs_direct_write_iomap_begin()
Date: Wed, 29 Jul 2026 20:01:48 +1000	[thread overview]
Message-ID: <20260729100629.1943710-5-dgc@kernel.org> (raw)
In-Reply-To: <20260729100629.1943710-1-dgc@kernel.org>

Extract the COW extent allocation and iomap setup logic from
xfs_direct_write_iomap_begin() into a new helper function
xfs_direct_write_cow_iomap_begin().

When the inode is a COW inode, the new function handles the entire
COW path: acquiring the ILOCK exclusively, reading the data fork
extent mapping, checking if COW is needed, allocating COW extents
via xfs_reflink_allocate_cow(), and setting up the iomap/srcmap for
the COW write.

The *nimaps output parameter tells the caller what to do next:
  - *nimaps == 0: COW was fully handled, iomap/srcmap are filled in,
    and the ILOCK has been released. The caller returns immediately.
  - *nimaps > 0: The extent is not shared. The imap is valid and the
    ILOCK is still held, so the caller continues with the normal
    allocation or overwrite IO path.

When the inode is not a COW inode, xfs_direct_write_iomap_begin()
handles the locking and extent lookup itself as before, taking
only a shared ILOCK.

This is a pure refactoring with no functional change, done to prepare
for reworking the COW allocation to handle transaction allocation and
retry logic at the xfs_direct_write_cow_iomap_begin() level.

Assisted-by: LLM
Signed-off-by: Dave Chinner <dgc@kernel.org>
---
 fs/xfs/xfs_iomap.c | 184 ++++++++++++++++++++++++++++++---------------
 1 file changed, 123 insertions(+), 61 deletions(-)

diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 1437ea93563c..df9fc7c6a4b9 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -848,6 +848,111 @@ xfs_bmap_hw_atomic_write_possible(
 	return len <= xfs_inode_buftarg(ip)->bt_awu_max;
 }
 
+/*
+ * Handle COW extent allocation and iomap setup for direct writes to reflinked
+ * files.
+ *
+ * The caller passes in an imap and nimaps that the COW allocation will fill
+ * with the data fork extent mapping. On return, *nimaps indicates whether the
+ * caller needs to continue with the normal IO path:
+ *
+ *   *nimaps == 0: COW was handled, iomap/srcmap are filled in, ILOCK released.
+ *                 Caller should return 0 immediately.
+ *   *nimaps > 0:  Extent is not shared, imap is valid, ILOCK is still held.
+ *                 Caller should continue with the normal IO path.
+ */
+static int
+xfs_direct_write_cow_iomap_begin(
+	struct xfs_inode	*ip,
+	loff_t			offset,
+	loff_t			length,
+	unsigned		flags,
+	struct iomap		*iomap,
+	struct iomap		*srcmap,
+	struct xfs_bmbt_irec	*imap,
+	int			*nimaps,
+	unsigned int		*lockmode,
+	u16			iomap_flags)
+{
+	struct xfs_mount	*mp = ip->i_mount;
+	struct xfs_bmbt_irec	cmap;
+	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
+	xfs_fileoff_t		end_fsb = xfs_iomap_end_fsb(mp, offset, length);
+	bool			shared = false;
+	int			error;
+	u64			seq;
+
+	*lockmode = XFS_ILOCK_EXCL;
+
+relock:
+	error = xfs_ilock_for_iomap(ip, flags, lockmode);
+	if (error)
+		return error;
+
+	/*
+	 * The reflink iflag could have changed since the earlier unlocked
+	 * check, check if it again and relock if needed.
+	 */
+	if (xfs_is_cow_inode(ip) && *lockmode == XFS_ILOCK_SHARED) {
+		xfs_iunlock(ip, *lockmode);
+		*lockmode = XFS_ILOCK_EXCL;
+		goto relock;
+	}
+
+	*nimaps = 1;
+	error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, imap,
+			       nimaps, 0);
+	if (error)
+		goto out_unlock;
+
+	if (!imap_needs_cow(ip, flags, imap, *nimaps))
+		return 0;
+
+	error = -EAGAIN;
+	if (flags & IOMAP_NOWAIT)
+		goto out_unlock;
+
+	/* may drop and re-acquire the ilock */
+	error = xfs_reflink_allocate_cow(ip, imap, &cmap, &shared,
+			lockmode,
+			(flags & IOMAP_DIRECT) || IS_DAX(VFS_I(ip)));
+	if (error)
+		goto out_unlock;
+
+	if (!shared)
+		return 0;
+
+	if ((flags & IOMAP_ATOMIC) &&
+	    !xfs_bmap_hw_atomic_write_possible(ip, &cmap,
+			offset_fsb, end_fsb)) {
+		error = -ENOPROTOOPT;
+		goto out_unlock;
+	}
+
+	/*
+	 * COW extent found and allocated. Set up iomap/srcmap and return
+	 * with *nimaps = 0 to tell the caller the COW path is complete.
+	 */
+	*nimaps = 0;
+	length = XFS_FSB_TO_B(mp, cmap.br_startoff + cmap.br_blockcount);
+	trace_xfs_iomap_found(ip, offset, length - offset, XFS_COW_FORK,
+			&cmap);
+	if (imap->br_startblock != HOLESTARTBLOCK) {
+		seq = xfs_iomap_inode_sequence(ip, 0);
+		error = xfs_bmbt_to_iomap(ip, srcmap, imap, flags, 0, seq);
+		if (error)
+			goto out_unlock;
+	}
+	seq = xfs_iomap_inode_sequence(ip, IOMAP_F_SHARED);
+	xfs_iunlock(ip, *lockmode);
+	return xfs_bmbt_to_iomap(ip, iomap, &cmap, flags, IOMAP_F_SHARED, seq);
+
+out_unlock:
+	if (*lockmode)
+		xfs_iunlock(ip, *lockmode);
+	return error;
+}
+
 static int
 xfs_direct_write_iomap_begin(
 	struct inode		*inode,
@@ -859,12 +964,11 @@ xfs_direct_write_iomap_begin(
 {
 	struct xfs_inode	*ip = XFS_I(inode);
 	struct xfs_mount	*mp = ip->i_mount;
-	struct xfs_bmbt_irec	imap, cmap;
+	struct xfs_bmbt_irec	imap;
 	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
 	xfs_fileoff_t		end_fsb = xfs_iomap_end_fsb(mp, offset, length);
 	xfs_fileoff_t		orig_end_fsb = end_fsb;
 	int			nimaps = 1, error = 0;
-	bool			shared = false;
 	u16			iomap_flags = 0;
 	bool			needs_alloc;
 	unsigned int		lockmode;
@@ -887,57 +991,28 @@ xfs_direct_write_iomap_begin(
 	if (flags & IOMAP_ATOMIC)
 		iomap_flags |= IOMAP_F_ATOMIC_BIO;
 
-	/*
-	 * COW writes may allocate delalloc space or convert unwritten COW
-	 * extents, so we need to make sure to take the lock exclusively here.
-	 */
-	if (xfs_is_cow_inode(ip))
-		lockmode = XFS_ILOCK_EXCL;
-	else
-		lockmode = XFS_ILOCK_SHARED;
-
-relock:
-	error = xfs_ilock_for_iomap(ip, flags, &lockmode);
-	if (error)
-		return error;
-
-	/*
-	 * The reflink iflag could have changed since the earlier unlocked
-	 * check, check if it again and relock if needed.
-	 */
-	if (xfs_is_cow_inode(ip) && lockmode == XFS_ILOCK_SHARED) {
-		xfs_iunlock(ip, lockmode);
-		lockmode = XFS_ILOCK_EXCL;
-		goto relock;
-	}
+	if (xfs_is_cow_inode(ip)) {
+		error = xfs_direct_write_cow_iomap_begin(ip, offset, length,
+				flags, iomap, srcmap, &imap, &nimaps,
+				&lockmode, iomap_flags);
+		if (error)
+			return error;
+		if (!nimaps)
+			return 0;
 
-	error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
-			       &nimaps, 0);
-	if (error)
-		goto out_unlock;
+		end_fsb = imap.br_startoff + imap.br_blockcount;
+		length = XFS_FSB_TO_B(mp, end_fsb) - offset;
+	} else {
+		lockmode = XFS_ILOCK_SHARED;
 
-	if (imap_needs_cow(ip, flags, &imap, nimaps)) {
-		error = -EAGAIN;
-		if (flags & IOMAP_NOWAIT)
-			goto out_unlock;
+		error = xfs_ilock_for_iomap(ip, flags, &lockmode);
+		if (error)
+			return error;
 
-		/* may drop and re-acquire the ilock */
-		error = xfs_reflink_allocate_cow(ip, &imap, &cmap, &shared,
-				&lockmode,
-				(flags & IOMAP_DIRECT) || IS_DAX(inode));
+		error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
+				&imap, &nimaps, 0);
 		if (error)
 			goto out_unlock;
-		if (shared) {
-			if ((flags & IOMAP_ATOMIC) &&
-			    !xfs_bmap_hw_atomic_write_possible(ip, &cmap,
-					offset_fsb, end_fsb)) {
-				error = -ENOPROTOOPT;
-				goto out_unlock;
-			}
-			goto out_found_cow;
-		}
-		end_fsb = imap.br_startoff + imap.br_blockcount;
-		length = XFS_FSB_TO_B(mp, end_fsb) - offset;
 	}
 
 	needs_alloc = imap_needs_alloc(inode, flags, &imap, nimaps);
@@ -1022,19 +1097,6 @@ xfs_direct_write_iomap_begin(
 	return xfs_bmbt_to_iomap(ip, iomap, &imap, flags,
 				 iomap_flags | IOMAP_F_NEW, seq);
 
-out_found_cow:
-	length = XFS_FSB_TO_B(mp, cmap.br_startoff + cmap.br_blockcount);
-	trace_xfs_iomap_found(ip, offset, length - offset, XFS_COW_FORK, &cmap);
-	if (imap.br_startblock != HOLESTARTBLOCK) {
-		seq = xfs_iomap_inode_sequence(ip, 0);
-		error = xfs_bmbt_to_iomap(ip, srcmap, &imap, flags, 0, seq);
-		if (error)
-			goto out_unlock;
-	}
-	seq = xfs_iomap_inode_sequence(ip, IOMAP_F_SHARED);
-	xfs_iunlock(ip, lockmode);
-	return xfs_bmbt_to_iomap(ip, iomap, &cmap, flags, IOMAP_F_SHARED, seq);
-
 out_unlock:
 	if (lockmode)
 		xfs_iunlock(ip, lockmode);
-- 
2.55.0


  parent reply	other threads:[~2026-07-29 10:06 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-29 10:01 [RFC PATCH 00/33] XFS: Atomic multi-extent operations via rolling transactions Dave Chinner
2026-07-29 10:01 ` [PATCH 01/33] xfs: fix dirty transaction cancellation in xfs_bmapi_convert_one_delalloc Dave Chinner
2026-07-29 10:01 ` [PATCH 02/33] xfs: fix isize update in xfs_iomap_write_unwritten to track conversion progress Dave Chinner
2026-07-29 10:01 ` [PATCH 03/33] xfs: fix block reservation for zoned RT extent remapping Dave Chinner
2026-07-29 10:01 ` Dave Chinner [this message]
2026-07-29 10:01 ` [PATCH 05/33] xfs: plumb xfs_trans through xfs_reflink_allocate_cow and fill_cow_hole Dave Chinner
2026-07-29 10:01 ` [PATCH 06/33] xfs: teach xfs_reflink_fill_cow_hole() to use a caller-supplied transaction Dave Chinner
2026-07-29 10:01 ` [PATCH 07/33] xfs: add transaction retry infrastructure to xfs_direct_write_cow_iomap_begin Dave Chinner
2026-07-29 10:01 ` [PATCH 08/33] xfs: return -EAGAIN from xfs_reflink_allocate_cow for COW hole without transaction Dave Chinner
2026-07-29 10:01 ` [PATCH 09/33] xfs: remove internal transaction allocation from xfs_reflink_fill_cow_hole Dave Chinner
2026-07-29 10:01 ` [PATCH 10/33] xfs: use zero-block transaction with xfs_trans_reserve_more_inode for COW holes Dave Chinner
2026-07-29 10:01 ` [PATCH 11/33] xfs: change *tp to **tpp in COW allocation call chain Dave Chinner
2026-07-29 10:01 ` [PATCH 12/33] xfs: convert xfs_reflink_fill_delalloc to use rolling transactions Dave Chinner
2026-07-29 10:01 ` [PATCH 13/33] xfs: return -EAGAIN from xfs_reflink_allocate_cow for all allocation cases Dave Chinner
2026-07-29 10:01 ` [PATCH 14/33] xfs: remove dead internal transaction allocation from xfs_reflink_fill_delalloc Dave Chinner
2026-07-29 10:01 ` [PATCH 15/33] xfs: plumb struct xfs_trans *tp into xfs_bmapi_convert_one_delalloc Dave Chinner
2026-07-29 10:02 ` [PATCH 16/33] xfs: use rolling transaction in xfs_bmapi_convert_delalloc Dave Chinner
2026-07-29 10:02 ` [PATCH 17/33] xfs: remove dead internal transaction path from xfs_bmapi_convert_one_delalloc Dave Chinner
2026-07-29 10:02 ` [PATCH 18/33] xfs: add block reservation renewal to xfs_defer_finish Dave Chinner
2026-07-29 10:02 ` [PATCH 19/33] xfs: factor out xfs_iomap_write_unwritten_one helper Dave Chinner
2026-07-29 10:02 ` [PATCH 20/33] xfs: convert xfs_iomap_write_unwritten to rolling transactions Dave Chinner
2026-07-29 10:02 ` [PATCH 21/33] xfs: plumb struct xfs_trans *tp into xfs_reflink_end_cow_extent Dave Chinner
2026-07-29 10:02 ` [PATCH 22/33] xfs: convert xfs_reflink_end_cow to rolling transactions Dave Chinner
2026-07-29 10:02 ` [PATCH 23/33] xfs: remove xfs_reflink_end_cow_extent wrapper and rename locked variant Dave Chinner
2026-07-29 10:02 ` [PATCH 24/33] xfs: convert xfs_zoned_end_io to rolling transactions Dave Chinner
2026-07-29 10:02 ` [PATCH 25/33] xfs: plumb struct xfs_trans *tp into xfs_iomap_write_direct Dave Chinner
2026-07-29 10:02 ` [PATCH 26/33] xfs: make xfs_iomap_write_direct fill in the iomap directly Dave Chinner
2026-07-29 10:02 ` [PATCH 27/33] xfs: plumb struct xfs_trans **tpp into xfs_direct_write_cow_iomap_begin Dave Chinner
2026-07-29 10:02 ` [PATCH 28/33] xfs: introduce struct xfs_direct_write_args for direct write call chain Dave Chinner
2026-07-29 10:02 ` [PATCH 29/33] xfs: convert xfs_direct_write_iomap_begin to use dwa struct throughout Dave Chinner
2026-07-29 10:02 ` [PATCH 30/33] xfs: restructure xfs_direct_write_iomap_begin with unified retry loop Dave Chinner
2026-07-29 10:02 ` [PATCH 31/33] xfs: clean up xfs_direct_write_cow_iomap_begin after restructure Dave Chinner
2026-07-29 10:02 ` [PATCH 32/33] xfs: make pNFS block allocation atomic with inode update Dave Chinner
2026-07-29 10:02 ` [PATCH 33/33] xfs: remove dead internal transaction path from xfs_iomap_write_direct Dave Chinner

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=20260729100629.1943710-5-dgc@kernel.org \
    --to=dgc@kernel.org \
    --cc=cem@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 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.