]> git.ipfire.org Git - thirdparty/kernel/stable.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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Nov 2025 20:37:46 +0000 (15:37 -0500)
commit 8d54eacd82a0623a963e0c150ad3b02970638b0d upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/xfs/xfs_iomap.c

index 2a74f2957341032403c2e32ce704ac120c4c11b2..a4a22975c7cc9a761a034686343ccbb139fe4d69 100644 (file)
@@ -1121,7 +1121,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) {
@@ -1132,6 +1132,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;
        }
@@ -1160,8 +1162,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;
        }
 
@@ -1201,6 +1205,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;