[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