From e101b9872f9b3f6c5e128f29d7c3bb91faca362b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:20 -0500 Subject: [PATCH 03/14] block: Let bdrv_co_is_zero_fast consolidate adjacent extents RH-Author: Eric Blake RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors RH-Jira: RHEL-88435 RHEL-88437 RH-Acked-by: Stefan Hajnoczi RH-Acked-by: Miroslav Rezanina RH-Commit: [3/14] 4520f7ef5bcc5803413541e6f48bf750af3d31e0 (ebblake/centos-qemu-kvm) Some BDS drivers have a cap on how much block status they can supply in one query (for example, NBD talking to an older server cannot inspect more than 4G per query; and qcow2 tends to cap its answers rather than cross a cluster boundary of an L1 table). Although the existing callers of bdrv_co_is_zero_fast are not passing in that large of a 'bytes' parameter, an upcoming caller wants to query the entire image at once, and will thus benefit from being able to treat adjacent zero regions in a coalesced manner, rather than claiming the region is non-zero merely because pnum was truncated and didn't match the incoming bytes. While refactoring this into a loop, note that there is no need to assign pnum prior to calling bdrv_co_common_block_status_above() (it is guaranteed to be assigned deeper in the callstack). Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-18-eblake@redhat.com> (cherry picked from commit 31bf15d97dd1d205a3b264675f9a1b3bd1939068) Jira: https://issues.redhat.com/browse/RHEL-88435 Jira: https://issues.redhat.com/browse/RHEL-88437 Signed-off-by: Eric Blake --- block/io.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/block/io.c b/block/io.c index e328402adc..64f4b1d22a 100644 --- a/block/io.c +++ b/block/io.c @@ -2751,28 +2751,31 @@ int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, int64_t offset, * by @offset and @bytes is known to read as zeroes. * Return 1 if that is the case, 0 otherwise and -errno on error. * This test is meant to be fast rather than accurate so returning 0 - * does not guarantee non-zero data. + * does not guarantee non-zero data; but a return of 1 is reliable. */ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes) { int ret; - int64_t pnum = bytes; + int64_t pnum; IO_CODE(); - if (!bytes) { - return 1; - } - - ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO, - offset, bytes, &pnum, NULL, NULL, - NULL); + while (bytes) { + ret = bdrv_co_common_block_status_above(bs, NULL, false, + BDRV_WANT_ZERO, offset, bytes, + &pnum, NULL, NULL, NULL); - if (ret < 0) { - return ret; + if (ret < 0) { + return ret; + } + if (!(ret & BDRV_BLOCK_ZERO)) { + return 0; + } + offset += pnum; + bytes -= pnum; } - return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); + return 1; } int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, -- 2.39.3