[PATCH 12/38] xfs: add transaction retry infrastructure to xfs_direct_write_cow_iomap_begin

Dave Chinner <[email protected]>
Newsgroups org.kernel.vger.linux-xfs
Message-ID <[email protected]>
Add the infrastructure for handling COW extent allocation retries with
a pre-allocated transaction in xfs_direct_write_cow_iomap_begin().

When xfs_reflink_allocate_cow() returns -EAGAIN (indicating it needs
a transaction to proceed), the function drops the ILOCK, allocates a
zero-block transaction via xfs_trans_alloc_inode() (which reacquires
the ILOCK), and jumps to retry: to re-read the imap via
xfs_bmapi_read() since the extent tree may have changed while the
ILOCK was not held.

The initial xfs_ilock_for_iomap() call is done once before the retry
label since we always enter with ILOCK_EXCL and xfs_trans_alloc_inode()
re-acquires ILOCK_EXCL on the retry path.

The old relock label and shared-to-exclusive lock upgrade logic is
removed as it is unnecessary - this function always takes ILOCK_EXCL.

Nothing returns -EAGAIN from xfs_reflink_allocate_cow() yet, so there
is no functional change.

Assisted-by: LLM
Signed-off-by: Dave Chinner <[email protected]>
---
 fs/xfs/xfs_iomap.c | 66 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 53 insertions(+), 13 deletions(-)

diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index e4e64412b431..017355372a44 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -876,6 +876,7 @@ xfs_direct_write_cow_iomap_begin(
 {
 	struct xfs_mount	*mp = ip->i_mount;
 	struct xfs_bmbt_irec	cmap;
+	struct xfs_trans	*tp = NULL;
 	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;
@@ -884,41 +885,78 @@ xfs_direct_write_cow_iomap_begin(
 
 	*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;
-	}
-
+retry:
 	*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))
+	if (!imap_needs_cow(ip, flags, imap, *nimaps)) {
+		/*
+		 * Extent is not shared - return the imap and ILOCK to the
+		 * caller for normal IO path processing.
+		 */
+		if (tp) {
+			xfs_trans_cancel(tp);
+			tp = NULL;
+		}
 		return 0;
+	}
 
 	error = -EAGAIN;
 	if (flags & IOMAP_NOWAIT)
 		goto out_unlock;
 
 	/* may drop and re-acquire the ilock */
-	error = xfs_reflink_allocate_cow(NULL, ip, imap, &cmap, &shared,
+	error = xfs_reflink_allocate_cow(tp, ip, imap, &cmap, &shared,
 			lockmode,
 			(flags & IOMAP_DIRECT) || IS_DAX(VFS_I(ip)));
+	if (error == -EAGAIN) {
+		/*
+		 * COW allocation needs a transaction. Drop the ILOCK and
+		 * allocate a transaction, which will re-acquire the ILOCK.
+		 * Then retry the imap lookup since the extent tree may have
+		 * changed while the ILOCK was not held.
+		 */
+		xfs_filblks_t		resaligned;
+		unsigned int		dblocks, rblocks;
+
+		ASSERT(!tp);
+
+		xfs_iunlock(ip, *lockmode);
+
+		resaligned = xfs_aligned_fsb_count(offset_fsb,
+				end_fsb - offset_fsb, xfs_get_cowextsz_hint(ip));
+		if (XFS_IS_REALTIME_INODE(ip)) {
+			dblocks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
+			rblocks = resaligned;
+		} else {
+			dblocks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned);
+			rblocks = 0;
+		}
+
+		error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_write,
+				dblocks, rblocks, false, &tp);
+		if (error)
+			return error;
+
+		goto retry;
+	}
 	if (error)
 		goto out_unlock;
 
+	if (tp) {
+		error = xfs_trans_commit(tp);
+		tp = NULL;
+		if (error)
+			goto out_unlock;
+	}
+
 	if (!shared)
 		return 0;
 
@@ -948,6 +986,8 @@ xfs_direct_write_cow_iomap_begin(
 	return xfs_bmbt_to_iomap(ip, iomap, &cmap, flags, IOMAP_F_SHARED, seq);
 
 out_unlock:
+	if (tp)
+		xfs_trans_cancel(tp);
 	if (*lockmode)
 		xfs_iunlock(ip, *lockmode);
 	return error;
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.