mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 00:03:54 -06:00
block: Add new bdrv_co_is_all_zeroes() function
There are some optimizations that require knowing if an image starts out as reading all zeroes, such as making blockdev-mirror faster by skipping the copying of source zeroes to the destination. The existing bdrv_co_is_zero_fast() is a good building block for answering this question, but it tends to give an answer of 0 for a file we just created via QMP 'blockdev-create' or similar (such as 'qemu-img create -f raw'). Why? Because file-posix.c insists on allocating a tiny header to any file rather than leaving it 100% sparse, due to some filesystems that are unable to answer alignment probes on a hole. But teaching file-posix.c to read the tiny header doesn't scale - the problem of a small header is also visible when libvirt sets up an NBD client to a just-created file on a migration destination host. So, we need a wrapper function that handles a bit more complexity in a common manner for all block devices - when the BDS is mostly a hole, but has a small non-hole header, it is still worth the time to read that header and check if it reads as all zeroes before giving up and returning a pessimistic answer. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Message-ID: <20250509204341.3553601-19-eblake@redhat.com>
This commit is contained in:
parent
31bf15d97d
commit
5272609670
2 changed files with 64 additions and 0 deletions
62
block/io.c
62
block/io.c
|
@ -38,10 +38,14 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "system/replay.h"
|
||||
#include "qemu/units.h"
|
||||
|
||||
/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */
|
||||
#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS)
|
||||
|
||||
/* Maximum read size for checking if data reads as zero, in bytes */
|
||||
#define MAX_ZERO_CHECK_BUFFER (128 * KiB)
|
||||
|
||||
static void coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_parent_cb_resize(BlockDriverState *bs);
|
||||
|
||||
|
@ -2778,6 +2782,64 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check @bs (and its backing chain) to see if the entire image 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; however, a return of 1 is reliable,
|
||||
* and this function can report 1 in more cases than bdrv_co_is_zero_fast.
|
||||
*/
|
||||
int coroutine_fn bdrv_co_is_all_zeroes(BlockDriverState *bs)
|
||||
{
|
||||
int ret;
|
||||
int64_t pnum, bytes;
|
||||
char *buf;
|
||||
QEMUIOVector local_qiov;
|
||||
IO_CODE();
|
||||
|
||||
bytes = bdrv_co_getlength(bs);
|
||||
if (bytes < 0) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* First probe - see if the entire image reads as zero */
|
||||
ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO,
|
||||
0, bytes, &pnum, NULL, NULL,
|
||||
NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret & BDRV_BLOCK_ZERO) {
|
||||
return bdrv_co_is_zero_fast(bs, pnum, bytes - pnum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Because of the way 'blockdev-create' works, raw files tend to
|
||||
* be created with a non-sparse region at the front to make
|
||||
* alignment probing easier. If the block starts with only a
|
||||
* small allocated region, it is still worth the effort to see if
|
||||
* the rest of the image is still sparse, coupled with manually
|
||||
* reading the first region to see if it reads zero after all.
|
||||
*/
|
||||
if (pnum > MAX_ZERO_CHECK_BUFFER) {
|
||||
return 0;
|
||||
}
|
||||
ret = bdrv_co_is_zero_fast(bs, pnum, bytes - pnum);
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
/* Only the head of the image is unknown, and it's small. Read it. */
|
||||
buf = qemu_blockalign(bs, pnum);
|
||||
qemu_iovec_init_buf(&local_qiov, buf, pnum);
|
||||
ret = bdrv_driver_preadv(bs, 0, pnum, &local_qiov, 0, 0);
|
||||
if (ret >= 0) {
|
||||
ret = buffer_is_zero(buf, pnum);
|
||||
}
|
||||
qemu_vfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
|
|
|
@ -161,6 +161,8 @@ bdrv_is_allocated_above(BlockDriverState *bs, BlockDriverState *base,
|
|||
|
||||
int coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes);
|
||||
int coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_co_is_all_zeroes(BlockDriverState *bs);
|
||||
|
||||
int GRAPH_RDLOCK
|
||||
bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue