xfs: fix XFS_ERRTAG_FORCE_ZERO_RANGE for zoned file system

The new XFS_ERRTAG_FORCE_ZERO_RANGE error tag added by commit
ea9989668081 ("xfs: error tag to force zeroing on debug kernels") fails
to account for the zoned space reservation rules and this reliably fails
xfs/131 because the zeroing operation returns -EIO.

Fix this by reserving enough space to zero the entire range, which
requires a bit of (fairly ugly) reshuffling to do the error injection
early enough to affect the space reservation.

Fixes: ea9989668081 ("xfs: error tag to force zeroing on debug kernels")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
Christoph Hellwig
2025-12-15 07:05:46 +01:00
committed by Carlos Maiolino
parent fc40459de8
commit 8dc15b7a6e

View File

@@ -1240,6 +1240,38 @@ xfs_falloc_insert_range(
return xfs_insert_file_space(XFS_I(inode), offset, len);
}
/*
* For various operations we need to zero up to one block at each end of
* the affected range. For zoned file systems this will require a space
* allocation, for which we need a reservation ahead of time.
*/
#define XFS_ZONED_ZERO_EDGE_SPACE_RES 2
/*
* Zero range implements a full zeroing mechanism but is only used in limited
* situations. It is more efficient to allocate unwritten extents than to
* perform zeroing here, so use an errortag to randomly force zeroing on DEBUG
* kernels for added test coverage.
*
* On zoned file systems, the error is already injected by
* xfs_file_zoned_fallocate, which then reserves the additional space needed.
* We only check for this extra space reservation here.
*/
static inline bool
xfs_falloc_force_zero(
struct xfs_inode *ip,
struct xfs_zone_alloc_ctx *ac)
{
if (xfs_is_zoned_inode(ip)) {
if (ac->reserved_blocks > XFS_ZONED_ZERO_EDGE_SPACE_RES) {
ASSERT(IS_ENABLED(CONFIG_XFS_DEBUG));
return true;
}
return false;
}
return XFS_TEST_ERROR(ip->i_mount, XFS_ERRTAG_FORCE_ZERO_RANGE);
}
/*
* Punch a hole and prealloc the range. We use a hole punch rather than
* unwritten extent conversion for two reasons:
@@ -1268,14 +1300,7 @@ xfs_falloc_zero_range(
if (error)
return error;
/*
* Zero range implements a full zeroing mechanism but is only used in
* limited situations. It is more efficient to allocate unwritten
* extents than to perform zeroing here, so use an errortag to randomly
* force zeroing on DEBUG kernels for added test coverage.
*/
if (XFS_TEST_ERROR(ip->i_mount,
XFS_ERRTAG_FORCE_ZERO_RANGE)) {
if (xfs_falloc_force_zero(ip, ac)) {
error = xfs_zero_range(ip, offset, len, ac, NULL);
} else {
error = xfs_free_file_space(ip, offset, len, ac);
@@ -1423,13 +1448,26 @@ xfs_file_zoned_fallocate(
{
struct xfs_zone_alloc_ctx ac = { };
struct xfs_inode *ip = XFS_I(file_inode(file));
struct xfs_mount *mp = ip->i_mount;
xfs_filblks_t count_fsb;
int error;
error = xfs_zoned_space_reserve(ip->i_mount, 2, XFS_ZR_RESERVED, &ac);
/*
* If full zeroing is forced by the error injection knob, we need a
* space reservation that covers the entire range. See the comment in
* xfs_zoned_write_space_reserve for the rationale for the calculation.
* Otherwise just reserve space for the two boundary blocks.
*/
count_fsb = XFS_ZONED_ZERO_EDGE_SPACE_RES;
if ((mode & FALLOC_FL_MODE_MASK) == FALLOC_FL_ZERO_RANGE &&
XFS_TEST_ERROR(mp, XFS_ERRTAG_FORCE_ZERO_RANGE))
count_fsb += XFS_B_TO_FSB(mp, len) + 1;
error = xfs_zoned_space_reserve(mp, count_fsb, XFS_ZR_RESERVED, &ac);
if (error)
return error;
error = __xfs_file_fallocate(file, mode, offset, len, &ac);
xfs_zoned_space_unreserve(ip->i_mount, &ac);
xfs_zoned_space_unreserve(mp, &ac);
return error;
}