]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xfs: fix delalloc write failures in software-provided atomic writes
authorDarrick J. Wong <djwong@kernel.org>
Wed, 5 Nov 2025 00:12:00 +0000 (16:12 -0800)
committerCarlos Maiolino <cem@kernel.org>
Wed, 5 Nov 2025 15:52:49 +0000 (16:52 +0100)
With the 20 Oct 2025 release of fstests, generic/521 fails for me on
regular (aka non-block-atomic-writes) storage:

QA output created by 521
dowrite: write: Input/output error
LOG DUMP (8553 total operations):
1(  1 mod 256): SKIPPED (no operation)
2(  2 mod 256): WRITE    0x7e000 thru 0x8dfff (0x10000 bytes) HOLE
3(  3 mod 256): READ     0x69000 thru 0x79fff (0x11000 bytes)
4(  4 mod 256): FALLOC   0x53c38 thru 0x5e853 (0xac1b bytes) INTERIOR
5(  5 mod 256): COPY 0x55000 thru 0x59fff (0x5000 bytes) to 0x25000 thru 0x29fff
6(  6 mod 256): WRITE    0x74000 thru 0x88fff (0x15000 bytes)
7(  7 mod 256): ZERO     0xedb1 thru 0x11693 (0x28e3 bytes)

with a warning in dmesg from iomap about XFS trying to give it a
delalloc mapping for a directio write.  Fix the software atomic write
iomap_begin code to convert the reservation into a written mapping.
This doesn't fix the data corruption problems reported by generic/760,
but it's a start.

Cc: stable@vger.kernel.org # v6.16
Fixes: bd1d2c21d5d249 ("xfs: add xfs_atomic_write_cow_iomap_begin()")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: John Garry <john.g.garry@oracle.com>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
fs/xfs/xfs_iomap.c

index d3f6e3e42a11913f00b79a439d74dcc2bbda6389..788bfdce608a7d33bc1e8e98d529b49b99c3bd13 100644 (file)
@@ -1130,7 +1130,7 @@ xfs_atomic_write_cow_iomap_begin(
                return -EAGAIN;
 
        trace_xfs_iomap_atomic_write_cow(ip, offset, length);
-
+retry:
        xfs_ilock(ip, XFS_ILOCK_EXCL);
 
        if (!ip->i_cowfp) {
@@ -1141,6 +1141,8 @@ xfs_atomic_write_cow_iomap_begin(
        if (!xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &cmap))
                cmap.br_startoff = end_fsb;
        if (cmap.br_startoff <= offset_fsb) {
+               if (isnullstartblock(cmap.br_startblock))
+                       goto convert_delay;
                xfs_trim_extent(&cmap, offset_fsb, count_fsb);
                goto found;
        }
@@ -1169,8 +1171,10 @@ xfs_atomic_write_cow_iomap_begin(
        if (!xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &cmap))
                cmap.br_startoff = end_fsb;
        if (cmap.br_startoff <= offset_fsb) {
-               xfs_trim_extent(&cmap, offset_fsb, count_fsb);
                xfs_trans_cancel(tp);
+               if (isnullstartblock(cmap.br_startblock))
+                       goto convert_delay;
+               xfs_trim_extent(&cmap, offset_fsb, count_fsb);
                goto found;
        }
 
@@ -1210,6 +1214,19 @@ found:
        xfs_iunlock(ip, XFS_ILOCK_EXCL);
        return xfs_bmbt_to_iomap(ip, iomap, &cmap, flags, IOMAP_F_SHARED, seq);
 
+convert_delay:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       error = xfs_bmapi_convert_delalloc(ip, XFS_COW_FORK, offset, iomap,
+                       NULL);
+       if (error)
+               return error;
+
+       /*
+        * Try the lookup again, because the delalloc conversion might have
+        * turned the COW mapping into unwritten, but we need it to be in
+        * written state.
+        */
+       goto retry;
 out_unlock:
        xfs_iunlock(ip, XFS_ILOCK_EXCL);
        return error;