diff mbox series

[28/37] xfs: ensure st_blocks never goes to zero during COW writes

Message ID 172954783894.34558.13885243332803707200.stgit@frogsfrogsfrogs (mailing list archive)
State New
Headers show
Series [01/37] libxfs: require -std=gnu11 for compilation by default | expand

Commit Message

Darrick J. Wong Oct. 21, 2024, 10:06 p.m. UTC
From: Christoph Hellwig <hch@lst.de>

Source kernel commit: 90fa22da6d6b41dc17435aff7b800f9ca3c00401

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>
---
 libxfs/defer_item.c |   14 ++++++++++++++
 libxfs/xfs_bmap.c   |    1 +
 2 files changed, 15 insertions(+)
diff mbox series

Patch

diff --git a/libxfs/defer_item.c b/libxfs/defer_item.c
index 98a291c7b785e1..2b48ed14d67bcb 100644
--- a/libxfs/defer_item.c
+++ b/libxfs/defer_item.c
@@ -520,6 +520,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);
 }
 
@@ -541,6 +552,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);
 }
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 4ee8d9b07a0ca7..c014325a5d7d9c 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -4850,6 +4850,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)