[PATCH 01/33] xfs: fix dirty transaction cancellation in xfs_bmapi_convert_one_delalloc

Dave Chinner <[email protected]> Wed, 29 Jul 2026 20:01:45 +1000
Newsgroups org.kernel.vger.linux-xfs
Message-ID <[email protected]>
xfs_bmapi_convert_one_delalloc() calls xfs_iext_count_extend() before
checking whether there is actually a delalloc extent to convert. If
xfs_iext_count_extend() modifies the inode's extent count fields it
will dirty the transaction. If the subsequent extent lookup finds
nothing to convert (e.g. a racing COW completion moved the extent to
the data fork), the function returns -EAGAIN and the caller cancels
the transaction. Cancelling a dirty transaction triggers a filesystem
shutdown.

Fix this by moving the extent lookup and validation ahead of the
xfs_iext_count_extend() call. This ensures that if there is no extent
to convert, or the extent has already been converted by a racing
thread, the function returns before the transaction is dirtied and
can be safely cancelled.

Fixes: 4f86bb4b66c9 ("xfs: Conditionally upgrade existing inodes to use large extent counters")
Assisted-by: LLM
Signed-off-by: Dave Chinner <[email protected]>
---
 fs/xfs/libxfs/xfs_bmap.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index d64defeda645..47c4d5c52b95 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -4380,11 +4380,11 @@ xfs_bmapi_convert_one_delalloc(
 	xfs_ilock(ip, XFS_ILOCK_EXCL);
 	xfs_trans_ijoin(tp, ip, 0);
 
-	error = xfs_iext_count_extend(tp, ip, whichfork,
-			XFS_IEXT_ADD_NOSPLIT_CNT);
-	if (error)
-		goto out_trans_cancel;
-
+	/*
+	 * Look up the extent before extending the extent count so that we
+	 * don't dirty the transaction if there is nothing to convert. A
+	 * dirty transaction cancellation on -EAGAIN would shutdown the fs.
+	 */
 	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &bma.icur, &bma.got) ||
 	    bma.got.br_startoff > offset_fsb) {
 		/*
@@ -4409,6 +4409,11 @@ xfs_bmapi_convert_one_delalloc(
 		goto out_trans_cancel;
 	}
 
+	error = xfs_iext_count_extend(tp, ip, whichfork,
+			XFS_IEXT_ADD_NOSPLIT_CNT);
+	if (error)
+		goto out_trans_cancel;
+
 	bma.tp = tp;
 	bma.ip = ip;
 	bma.wasdel = true;
-- 
2.55.0