]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: ensure st_blocks never goes to zero during COW writes
authorChristoph Hellwig <hch@lst.de>
Tue, 27 Aug 2024 05:03:21 +0000 (07:03 +0200)
committerChandan Babu R <chandanbabu@kernel.org>
Tue, 3 Sep 2024 04:37:47 +0000 (10:07 +0530)
COW writes remove the amount overwritten either directly for delalloc
reservations, or in earlier deferred transactions than adding the new
amount back in the bmap map transaction.  This means st_blocks on an
inode where all data is overwritten using the COW path can temporarily
show a 0 st_blocks.  This can easily be reproduced with the pending
zoned device support where all writes use this path and trips the
check in generic/615, but could also happen on a reflink file without
that.

Fix this by temporarily add the pending blocks to be mapped to
i_delayed_blks while the item is queued.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
fs/xfs/libxfs/xfs_bmap.c
fs/xfs/xfs_bmap_item.c

index 28473b6a95cc7fe6d7579fa7e70d339d1f301a83..8090e8249116d0b24d506b495455b996be555eac 100644 (file)
@@ -4856,6 +4856,7 @@ xfs_bmapi_remap(
        }
 
        ip->i_nblocks += len;
+       ip->i_delayed_blks -= len; /* see xfs_bmap_defer_add */
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
        if (ifp->if_format == XFS_DINODE_FMT_BTREE)
index e224b49b7cff6d6274c9c4611f6d4166185bc831..35a8c1b8b3cb34e3033ec21363fe71d9f9a1592d 100644 (file)
@@ -346,6 +346,17 @@ xfs_bmap_defer_add(
        trace_xfs_bmap_defer(bi);
 
        xfs_bmap_update_get_group(tp->t_mountp, bi);
+
+       /*
+        * Ensure the deferred mapping is pre-recorded in i_delayed_blks.
+        *
+        * Otherwise stat can report zero blocks for an inode that actually has
+        * data when the entire mapping is in the process of being overwritten
+        * using the out of place write path. This is undone in xfs_bmapi_remap
+        * after it has incremented di_nblocks for a successful operation.
+        */
+       if (bi->bi_type == XFS_BMAP_MAP)
+               bi->bi_owner->i_delayed_blks += bi->bi_bmap.br_blockcount;
        xfs_defer_add(tp, &bi->bi_list, &xfs_bmap_update_defer_type);
 }
 
@@ -367,6 +378,9 @@ xfs_bmap_update_cancel_item(
 {
        struct xfs_bmap_intent          *bi = bi_entry(item);
 
+       if (bi->bi_type == XFS_BMAP_MAP)
+               bi->bi_owner->i_delayed_blks -= bi->bi_bmap.br_blockcount;
+
        xfs_bmap_update_put_group(bi);
        kmem_cache_free(xfs_bmap_intent_cache, bi);
 }
@@ -464,6 +478,9 @@ xfs_bui_recover_work(
        bi->bi_owner = *ipp;
        xfs_bmap_update_get_group(mp, bi);
 
+       /* see xfs_bmap_defer_add for details */
+       if (bi->bi_type == XFS_BMAP_MAP)
+               bi->bi_owner->i_delayed_blks += bi->bi_bmap.br_blockcount;
        xfs_defer_add_item(dfp, &bi->bi_list);
        return bi;
 }