* Mon May 26 2025 Miroslav Rezanina <mrezanin@redhat.com> - 10.0.0-4
- kvm-block-Expand-block-status-mode-from-bool-to-flags.patch [RHEL-88435 RHEL-88437] - kvm-file-posix-gluster-Handle-zero-block-status-hint-bet.patch [RHEL-88435 RHEL-88437] - kvm-block-Let-bdrv_co_is_zero_fast-consolidate-adjacent-.patch [RHEL-88435 RHEL-88437] - kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch [RHEL-88435 RHEL-88437] - kvm-iotests-Improve-iotest-194-to-mirror-data.patch [RHEL-88435 RHEL-88437] - kvm-mirror-Minor-refactoring.patch [RHEL-88435 RHEL-88437] - kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch [RHEL-88435 RHEL-88437] - kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch [RHEL-88435 RHEL-88437] - kvm-mirror-Drop-redundant-zero_target-parameter.patch [RHEL-88435 RHEL-88437] - kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch [RHEL-88435 RHEL-88437] - kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch [RHEL-88435 RHEL-88437] - kvm-iotests-common.rc-add-disk_usage-function.patch [RHEL-88435 RHEL-88437] - kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch [RHEL-88435 RHEL-88437] - kvm-mirror-Reduce-I-O-when-destination-is-detect-zeroes-.patch [RHEL-88435 RHEL-88437] - Resolves: RHEL-88435 (--migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]) - Resolves: RHEL-88437 (Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1])
This commit is contained in:
parent
9c8774ec77
commit
f696e6c6e2
143
kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch
Normal file
143
kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch
Normal file
@ -0,0 +1,143 @@
|
||||
From 659dd2d1f7b1facbf9c548468c1b50237f7aa8e4 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:21 -0500
|
||||
Subject: [PATCH 04/14] block: Add new bdrv_co_is_all_zeroes() function
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [4/14] 404590dbec0b1113872a7eb1bfe5af0450fe6a28 (ebblake/centos-qemu-kvm)
|
||||
|
||||
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>
|
||||
(cherry picked from commit 52726096707c5c8b90597c445de897fa64d56e73)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/io.c | 62 ++++++++++++++++++++++++++++++++++++++++
|
||||
include/block/block-io.h | 2 ++
|
||||
2 files changed, 64 insertions(+)
|
||||
|
||||
diff --git a/block/io.c b/block/io.c
|
||||
index 64f4b1d22a..b6fc07e1dc 100644
|
||||
--- a/block/io.c
|
||||
+++ b/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)
|
||||
{
|
||||
diff --git a/include/block/block-io.h b/include/block/block-io.h
|
||||
index b49e0537dd..b99cc98d26 100644
|
||||
--- a/include/block/block-io.h
|
||||
+++ b/include/block/block-io.h
|
||||
@@ -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,
|
||||
--
|
||||
2.39.3
|
||||
|
689
kvm-block-Expand-block-status-mode-from-bool-to-flags.patch
Normal file
689
kvm-block-Expand-block-status-mode-from-bool-to-flags.patch
Normal file
@ -0,0 +1,689 @@
|
||||
From cb945ccd11d37c959f590ae5661ffe5b73f372a7 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:18 -0500
|
||||
Subject: [PATCH 01/14] block: Expand block status mode from bool to flags
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/14] 12507ca1dbe44640e4a8baded4d5afd5fb4ed615 (ebblake/centos-qemu-kvm)
|
||||
|
||||
This patch is purely mechanical, changing bool want_zero into an
|
||||
unsigned int for bitwise-or of flags. As of this patch, all
|
||||
implementations are unchanged (the old want_zero==true is now
|
||||
mode==BDRV_WANT_PRECISE which is a superset of BDRV_WANT_ZERO); but
|
||||
the callers in io.c that used to pass want_zero==false are now
|
||||
prepared for future driver changes that can now distinguish bewteen
|
||||
BDRV_WANT_ZERO vs. BDRV_WANT_ALLOCATED. The next patch will actually
|
||||
change the file-posix driver along those lines, now that we have
|
||||
more-specific hints.
|
||||
|
||||
As for the background why this patch is useful: right now, the
|
||||
file-posix driver recognizes that if allocation is being queried, the
|
||||
entire image can be reported as allocated (there is no backing file to
|
||||
refer to) - but this throws away information on whether the entire
|
||||
image reads as zero (trivially true if lseek(SEEK_HOLE) at offset 0
|
||||
returns -ENXIO, a bit more complicated to prove if the raw file was
|
||||
created with 'qemu-img create' since we intentionally allocate a small
|
||||
chunk of all-zero data to help with alignment probing). Later patches
|
||||
will add a generic algorithm for seeing if an entire file reads as
|
||||
zeroes.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-16-eblake@redhat.com>
|
||||
(cherry picked from commit c33159dec79069514f78faecfe268439226b0f5b)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/blkdebug.c | 6 ++--
|
||||
block/copy-before-write.c | 4 +--
|
||||
block/coroutines.h | 4 +--
|
||||
block/file-posix.c | 4 +--
|
||||
block/gluster.c | 4 +--
|
||||
block/io.c | 51 ++++++++++++++++----------------
|
||||
block/iscsi.c | 6 ++--
|
||||
block/nbd.c | 4 +--
|
||||
block/null.c | 6 ++--
|
||||
block/parallels.c | 6 ++--
|
||||
block/qcow.c | 2 +-
|
||||
block/qcow2.c | 6 ++--
|
||||
block/qed.c | 6 ++--
|
||||
block/quorum.c | 4 +--
|
||||
block/raw-format.c | 4 +--
|
||||
block/rbd.c | 6 ++--
|
||||
block/snapshot-access.c | 4 +--
|
||||
block/vdi.c | 4 +--
|
||||
block/vmdk.c | 2 +-
|
||||
block/vpc.c | 2 +-
|
||||
block/vvfat.c | 6 ++--
|
||||
include/block/block-common.h | 11 +++++++
|
||||
include/block/block_int-common.h | 27 +++++++++--------
|
||||
include/block/block_int-io.h | 4 +--
|
||||
tests/unit/test-block-iothread.c | 2 +-
|
||||
25 files changed, 99 insertions(+), 86 deletions(-)
|
||||
|
||||
diff --git a/block/blkdebug.c b/block/blkdebug.c
|
||||
index 1c1967f8e0..c54aee0c84 100644
|
||||
--- a/block/blkdebug.c
|
||||
+++ b/block/blkdebug.c
|
||||
@@ -751,9 +751,9 @@ blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-blkdebug_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
- BlockDriverState **file)
|
||||
+blkdebug_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes, int64_t *pnum,
|
||||
+ int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
int err;
|
||||
|
||||
diff --git a/block/copy-before-write.c b/block/copy-before-write.c
|
||||
index fd470f5f92..2badb3a885 100644
|
||||
--- a/block/copy-before-write.c
|
||||
+++ b/block/copy-before-write.c
|
||||
@@ -291,8 +291,8 @@ cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-cbw_co_snapshot_block_status(BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset, int64_t bytes,
|
||||
+cbw_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
diff --git a/block/coroutines.h b/block/coroutines.h
|
||||
index 79e5efbf75..892646bb7a 100644
|
||||
--- a/block/coroutines.h
|
||||
+++ b/block/coroutines.h
|
||||
@@ -47,7 +47,7 @@ int coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_co_common_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
bool include_base,
|
||||
- bool want_zero,
|
||||
+ unsigned int mode,
|
||||
int64_t offset,
|
||||
int64_t bytes,
|
||||
int64_t *pnum,
|
||||
@@ -78,7 +78,7 @@ int co_wrapper_mixed_bdrv_rdlock
|
||||
bdrv_common_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
bool include_base,
|
||||
- bool want_zero,
|
||||
+ unsigned int mode,
|
||||
int64_t offset,
|
||||
int64_t bytes,
|
||||
int64_t *pnum,
|
||||
diff --git a/block/file-posix.c b/block/file-posix.c
|
||||
index 0d85123d0f..0c6569742f 100644
|
||||
--- a/block/file-posix.c
|
||||
+++ b/block/file-posix.c
|
||||
@@ -3266,7 +3266,7 @@ static int find_allocation(BlockDriverState *bs, off_t start,
|
||||
* well exceed it.
|
||||
*/
|
||||
static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero,
|
||||
+ unsigned int mode,
|
||||
int64_t offset,
|
||||
int64_t bytes, int64_t *pnum,
|
||||
int64_t *map,
|
||||
@@ -3282,7 +3282,7 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (!want_zero) {
|
||||
+ if (mode != BDRV_WANT_PRECISE) {
|
||||
*pnum = bytes;
|
||||
*map = offset;
|
||||
*file = bs;
|
||||
diff --git a/block/gluster.c b/block/gluster.c
|
||||
index c6d25ae733..8197b0ecef 100644
|
||||
--- a/block/gluster.c
|
||||
+++ b/block/gluster.c
|
||||
@@ -1465,7 +1465,7 @@ exit:
|
||||
* (Based on raw_co_block_status() from file-posix.c.)
|
||||
*/
|
||||
static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero,
|
||||
+ unsigned int mode,
|
||||
int64_t offset,
|
||||
int64_t bytes,
|
||||
int64_t *pnum,
|
||||
@@ -1482,7 +1482,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (!want_zero) {
|
||||
+ if (mode != BDRV_WANT_PRECISE) {
|
||||
*pnum = bytes;
|
||||
*map = offset;
|
||||
*file = bs;
|
||||
diff --git a/block/io.c b/block/io.c
|
||||
index ccec11386b..e328402adc 100644
|
||||
--- a/block/io.c
|
||||
+++ b/block/io.c
|
||||
@@ -2364,10 +2364,8 @@ int bdrv_flush_all(void)
|
||||
* Drivers not implementing the functionality are assumed to not support
|
||||
* backing files, hence all their sectors are reported as allocated.
|
||||
*
|
||||
- * If 'want_zero' is true, the caller is querying for mapping
|
||||
- * purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and
|
||||
- * _ZERO where possible; otherwise, the result favors larger 'pnum',
|
||||
- * with a focus on accurate BDRV_BLOCK_ALLOCATED.
|
||||
+ * 'mode' serves as a hint as to which results are favored; see the
|
||||
+ * BDRV_WANT_* macros for details.
|
||||
*
|
||||
* If 'offset' is beyond the end of the disk image the return value is
|
||||
* BDRV_BLOCK_EOF and 'pnum' is set to 0.
|
||||
@@ -2387,7 +2385,7 @@ int bdrv_flush_all(void)
|
||||
* set to the host mapping and BDS corresponding to the guest offset.
|
||||
*/
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
|
||||
+bdrv_co_do_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
@@ -2476,7 +2474,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
|
||||
local_file = bs;
|
||||
local_map = aligned_offset;
|
||||
} else {
|
||||
- ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset,
|
||||
+ ret = bs->drv->bdrv_co_block_status(bs, mode, aligned_offset,
|
||||
aligned_bytes, pnum, &local_map,
|
||||
&local_file);
|
||||
|
||||
@@ -2488,10 +2486,10 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
|
||||
* the cache requires an RCU update, so double check here to avoid
|
||||
* such an update if possible.
|
||||
*
|
||||
- * Check want_zero, because we only want to update the cache when we
|
||||
+ * Check mode, because we only want to update the cache when we
|
||||
* have accurate information about what is zero and what is data.
|
||||
*/
|
||||
- if (want_zero &&
|
||||
+ if (mode == BDRV_WANT_PRECISE &&
|
||||
ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) &&
|
||||
QLIST_EMPTY(&bs->children))
|
||||
{
|
||||
@@ -2548,7 +2546,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
|
||||
|
||||
if (ret & BDRV_BLOCK_RAW) {
|
||||
assert(ret & BDRV_BLOCK_OFFSET_VALID && local_file);
|
||||
- ret = bdrv_co_do_block_status(local_file, want_zero, local_map,
|
||||
+ ret = bdrv_co_do_block_status(local_file, mode, local_map,
|
||||
*pnum, pnum, &local_map, &local_file);
|
||||
goto out;
|
||||
}
|
||||
@@ -2560,7 +2558,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
|
||||
|
||||
if (!cow_bs) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
- } else if (want_zero) {
|
||||
+ } else if (mode == BDRV_WANT_PRECISE) {
|
||||
int64_t size2 = bdrv_co_getlength(cow_bs);
|
||||
|
||||
if (size2 >= 0 && offset >= size2) {
|
||||
@@ -2569,14 +2567,14 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
|
||||
}
|
||||
}
|
||||
|
||||
- if (want_zero && ret & BDRV_BLOCK_RECURSE &&
|
||||
+ if (mode == BDRV_WANT_PRECISE && ret & BDRV_BLOCK_RECURSE &&
|
||||
local_file && local_file != bs &&
|
||||
(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) &&
|
||||
(ret & BDRV_BLOCK_OFFSET_VALID)) {
|
||||
int64_t file_pnum;
|
||||
int ret2;
|
||||
|
||||
- ret2 = bdrv_co_do_block_status(local_file, want_zero, local_map,
|
||||
+ ret2 = bdrv_co_do_block_status(local_file, mode, local_map,
|
||||
*pnum, &file_pnum, NULL, NULL);
|
||||
if (ret2 >= 0) {
|
||||
/* Ignore errors. This is just providing extra information, it
|
||||
@@ -2627,7 +2625,7 @@ int coroutine_fn
|
||||
bdrv_co_common_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
bool include_base,
|
||||
- bool want_zero,
|
||||
+ unsigned int mode,
|
||||
int64_t offset,
|
||||
int64_t bytes,
|
||||
int64_t *pnum,
|
||||
@@ -2654,7 +2652,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
- ret = bdrv_co_do_block_status(bs, want_zero, offset, bytes, pnum,
|
||||
+ ret = bdrv_co_do_block_status(bs, mode, offset, bytes, pnum,
|
||||
map, file);
|
||||
++*depth;
|
||||
if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) {
|
||||
@@ -2671,7 +2669,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
|
||||
for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base;
|
||||
p = bdrv_filter_or_cow_bs(p))
|
||||
{
|
||||
- ret = bdrv_co_do_block_status(p, want_zero, offset, bytes, pnum,
|
||||
+ ret = bdrv_co_do_block_status(p, mode, offset, bytes, pnum,
|
||||
map, file);
|
||||
++*depth;
|
||||
if (ret < 0) {
|
||||
@@ -2734,7 +2732,8 @@ int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
IO_CODE();
|
||||
- return bdrv_co_common_block_status_above(bs, base, false, true, offset,
|
||||
+ return bdrv_co_common_block_status_above(bs, base, false,
|
||||
+ BDRV_WANT_PRECISE, offset,
|
||||
bytes, pnum, map, file, NULL);
|
||||
}
|
||||
|
||||
@@ -2765,8 +2764,9 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
|
||||
return 1;
|
||||
}
|
||||
|
||||
- ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset,
|
||||
- bytes, &pnum, NULL, NULL, NULL);
|
||||
+ ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO,
|
||||
+ offset, bytes, &pnum, NULL, NULL,
|
||||
+ NULL);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -2782,9 +2782,9 @@ int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
|
||||
int64_t dummy;
|
||||
IO_CODE();
|
||||
|
||||
- ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset,
|
||||
- bytes, pnum ? pnum : &dummy, NULL,
|
||||
- NULL, NULL);
|
||||
+ ret = bdrv_co_common_block_status_above(bs, bs, true, BDRV_WANT_ALLOCATED,
|
||||
+ offset, bytes, pnum ? pnum : &dummy,
|
||||
+ NULL, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -2817,7 +2817,8 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *bs,
|
||||
int ret;
|
||||
IO_CODE();
|
||||
|
||||
- ret = bdrv_co_common_block_status_above(bs, base, include_base, false,
|
||||
+ ret = bdrv_co_common_block_status_above(bs, base, include_base,
|
||||
+ BDRV_WANT_ALLOCATED,
|
||||
offset, bytes, pnum, NULL, NULL,
|
||||
&depth);
|
||||
if (ret < 0) {
|
||||
@@ -3714,8 +3715,8 @@ bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes,
|
||||
}
|
||||
|
||||
int coroutine_fn
|
||||
-bdrv_co_snapshot_block_status(BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset, int64_t bytes,
|
||||
+bdrv_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
@@ -3733,7 +3734,7 @@ bdrv_co_snapshot_block_status(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
bdrv_inc_in_flight(bs);
|
||||
- ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes,
|
||||
+ ret = drv->bdrv_co_snapshot_block_status(bs, mode, offset, bytes,
|
||||
pnum, map, file);
|
||||
bdrv_dec_in_flight(bs);
|
||||
|
||||
diff --git a/block/iscsi.c b/block/iscsi.c
|
||||
index 2f0f4dac09..15b96ee880 100644
|
||||
--- a/block/iscsi.c
|
||||
+++ b/block/iscsi.c
|
||||
@@ -694,9 +694,9 @@ out_unlock:
|
||||
|
||||
|
||||
static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *pnum,
|
||||
- int64_t *map,
|
||||
+ unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes,
|
||||
+ int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
diff --git a/block/nbd.c b/block/nbd.c
|
||||
index 887841bc81..d5a2b21c6d 100644
|
||||
--- a/block/nbd.c
|
||||
+++ b/block/nbd.c
|
||||
@@ -1397,8 +1397,8 @@ nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status(
|
||||
- BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes,
|
||||
- int64_t *pnum, int64_t *map, BlockDriverState **file)
|
||||
+ BlockDriverState *bs, unsigned int mode, int64_t offset,
|
||||
+ int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
int ret, request_ret;
|
||||
NBDExtent64 extent = { 0 };
|
||||
diff --git a/block/null.c b/block/null.c
|
||||
index dc0b1fdbd9..4e448d593d 100644
|
||||
--- a/block/null.c
|
||||
+++ b/block/null.c
|
||||
@@ -227,9 +227,9 @@ static int null_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
}
|
||||
|
||||
static int coroutine_fn null_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *pnum,
|
||||
- int64_t *map,
|
||||
+ unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes,
|
||||
+ int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
diff --git a/block/parallels.c b/block/parallels.c
|
||||
index 347ca127f3..3a375e2a8a 100644
|
||||
--- a/block/parallels.c
|
||||
+++ b/block/parallels.c
|
||||
@@ -416,9 +416,9 @@ parallels_co_flush_to_os(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-parallels_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
- BlockDriverState **file)
|
||||
+parallels_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes, int64_t *pnum,
|
||||
+ int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
int count;
|
||||
diff --git a/block/qcow.c b/block/qcow.c
|
||||
index da8ad4d243..8a3e7591a9 100644
|
||||
--- a/block/qcow.c
|
||||
+++ b/block/qcow.c
|
||||
@@ -530,7 +530,7 @@ get_cluster_offset(BlockDriverState *bs, uint64_t offset, int allocate,
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-qcow_co_block_status(BlockDriverState *bs, bool want_zero,
|
||||
+qcow_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
int64_t offset, int64_t bytes, int64_t *pnum,
|
||||
int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||
index b6ade4755d..9fc96ba99a 100644
|
||||
--- a/block/qcow2.c
|
||||
+++ b/block/qcow2.c
|
||||
@@ -2147,9 +2147,9 @@ static void qcow2_join_options(QDict *options, QDict *old_options)
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
|
||||
- int64_t count, int64_t *pnum, int64_t *map,
|
||||
- BlockDriverState **file)
|
||||
+qcow2_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t count, int64_t *pnum,
|
||||
+ int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t host_offset;
|
||||
diff --git a/block/qed.c b/block/qed.c
|
||||
index ac24449ffb..4a36fb3929 100644
|
||||
--- a/block/qed.c
|
||||
+++ b/block/qed.c
|
||||
@@ -833,9 +833,9 @@ fail:
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-bdrv_qed_co_block_status(BlockDriverState *bs, bool want_zero, int64_t pos,
|
||||
- int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
- BlockDriverState **file)
|
||||
+bdrv_qed_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t pos, int64_t bytes, int64_t *pnum,
|
||||
+ int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
size_t len = MIN(bytes, SIZE_MAX);
|
||||
diff --git a/block/quorum.c b/block/quorum.c
|
||||
index 30747a6df9..ed8ce801ee 100644
|
||||
--- a/block/quorum.c
|
||||
+++ b/block/quorum.c
|
||||
@@ -1226,7 +1226,7 @@ static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
* region contains zeroes, and BDRV_BLOCK_DATA otherwise.
|
||||
*/
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-quorum_co_block_status(BlockDriverState *bs, bool want_zero,
|
||||
+quorum_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
int64_t offset, int64_t count,
|
||||
int64_t *pnum, int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
@@ -1238,7 +1238,7 @@ quorum_co_block_status(BlockDriverState *bs, bool want_zero,
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
int64_t bytes;
|
||||
ret = bdrv_co_common_block_status_above(s->children[i]->bs, NULL, false,
|
||||
- want_zero, offset, count,
|
||||
+ mode, offset, count,
|
||||
&bytes, NULL, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
quorum_report_bad(QUORUM_OP_TYPE_READ, offset, count,
|
||||
diff --git a/block/raw-format.c b/block/raw-format.c
|
||||
index e08526e2ec..df16ac1ea2 100644
|
||||
--- a/block/raw-format.c
|
||||
+++ b/block/raw-format.c
|
||||
@@ -283,8 +283,8 @@ fail:
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-raw_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
+raw_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
diff --git a/block/rbd.c b/block/rbd.c
|
||||
index af984fb7db..4f3d42a8e7 100644
|
||||
--- a/block/rbd.c
|
||||
+++ b/block/rbd.c
|
||||
@@ -1504,9 +1504,9 @@ static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len,
|
||||
}
|
||||
|
||||
static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *pnum,
|
||||
- int64_t *map,
|
||||
+ unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes,
|
||||
+ int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
diff --git a/block/snapshot-access.c b/block/snapshot-access.c
|
||||
index 71ac83c01f..17ed2402db 100644
|
||||
--- a/block/snapshot-access.c
|
||||
+++ b/block/snapshot-access.c
|
||||
@@ -41,11 +41,11 @@ snapshot_access_co_preadv_part(BlockDriverState *bs,
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
snapshot_access_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset,
|
||||
+ unsigned int mode, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum,
|
||||
int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
- return bdrv_co_snapshot_block_status(bs->file->bs, want_zero, offset,
|
||||
+ return bdrv_co_snapshot_block_status(bs->file->bs, mode, offset,
|
||||
bytes, pnum, map, file);
|
||||
}
|
||||
|
||||
diff --git a/block/vdi.c b/block/vdi.c
|
||||
index a2da6ecab0..3ddc62a569 100644
|
||||
--- a/block/vdi.c
|
||||
+++ b/block/vdi.c
|
||||
@@ -523,8 +523,8 @@ static int vdi_reopen_prepare(BDRVReopenState *state,
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-vdi_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
+vdi_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
|
||||
diff --git a/block/vmdk.c b/block/vmdk.c
|
||||
index 2adec49912..9c7ab037e1 100644
|
||||
--- a/block/vmdk.c
|
||||
+++ b/block/vmdk.c
|
||||
@@ -1777,7 +1777,7 @@ static inline uint64_t vmdk_find_offset_in_cluster(VmdkExtent *extent,
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-vmdk_co_block_status(BlockDriverState *bs, bool want_zero,
|
||||
+vmdk_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
int64_t offset, int64_t bytes, int64_t *pnum,
|
||||
int64_t *map, BlockDriverState **file)
|
||||
{
|
||||
diff --git a/block/vpc.c b/block/vpc.c
|
||||
index 0309e319f6..801ff5793f 100644
|
||||
--- a/block/vpc.c
|
||||
+++ b/block/vpc.c
|
||||
@@ -726,7 +726,7 @@ fail:
|
||||
}
|
||||
|
||||
static int coroutine_fn GRAPH_RDLOCK
|
||||
-vpc_co_block_status(BlockDriverState *bs, bool want_zero,
|
||||
+vpc_co_block_status(BlockDriverState *bs, unsigned int mode,
|
||||
int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
diff --git a/block/vvfat.c b/block/vvfat.c
|
||||
index 91d69b3cc8..814796d918 100644
|
||||
--- a/block/vvfat.c
|
||||
+++ b/block/vvfat.c
|
||||
@@ -3134,9 +3134,9 @@ vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
||||
}
|
||||
|
||||
static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset,
|
||||
- int64_t bytes, int64_t *n,
|
||||
- int64_t *map,
|
||||
+ unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes,
|
||||
+ int64_t *n, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
*n = bytes;
|
||||
diff --git a/include/block/block-common.h b/include/block/block-common.h
|
||||
index 0b831ef87b..c8c626daea 100644
|
||||
--- a/include/block/block-common.h
|
||||
+++ b/include/block/block-common.h
|
||||
@@ -333,6 +333,17 @@ typedef enum {
|
||||
#define BDRV_BLOCK_RECURSE 0x40
|
||||
#define BDRV_BLOCK_COMPRESSED 0x80
|
||||
|
||||
+/*
|
||||
+ * Block status hints: the bitwise-or of these flags emphasize what
|
||||
+ * the caller hopes to learn, and some drivers may be able to give
|
||||
+ * faster answers by doing less work when the hint permits.
|
||||
+ */
|
||||
+#define BDRV_WANT_ZERO BDRV_BLOCK_ZERO
|
||||
+#define BDRV_WANT_OFFSET_VALID BDRV_BLOCK_OFFSET_VALID
|
||||
+#define BDRV_WANT_ALLOCATED BDRV_BLOCK_ALLOCATED
|
||||
+#define BDRV_WANT_PRECISE (BDRV_WANT_ZERO | BDRV_WANT_OFFSET_VALID | \
|
||||
+ BDRV_WANT_OFFSET_VALID)
|
||||
+
|
||||
typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
|
||||
|
||||
typedef struct BDRVReopenState {
|
||||
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
|
||||
index ebb4e56a50..a9c0daa2a4 100644
|
||||
--- a/include/block/block_int-common.h
|
||||
+++ b/include/block/block_int-common.h
|
||||
@@ -608,15 +608,16 @@ struct BlockDriver {
|
||||
* according to the current layer, and should only need to set
|
||||
* BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID,
|
||||
* and/or BDRV_BLOCK_RAW; if the current layer defers to a backing
|
||||
- * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). See
|
||||
- * block.h for the overall meaning of the bits. As a hint, the
|
||||
- * flag want_zero is true if the caller cares more about precise
|
||||
- * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for
|
||||
- * overall allocation (favor larger *pnum, perhaps by reporting
|
||||
- * _DATA instead of _ZERO). The block layer guarantees input
|
||||
- * clamped to bdrv_getlength() and aligned to request_alignment,
|
||||
- * as well as non-NULL pnum, map, and file; in turn, the driver
|
||||
- * must return an error or set pnum to an aligned non-zero value.
|
||||
+ * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). The
|
||||
+ * caller will synthesize BDRV_BLOCK_ALLOCATED based on the
|
||||
+ * non-zero results. See block.h for the overall meaning of the
|
||||
+ * bits. As a hint, the flags in @mode may include a bitwise-or
|
||||
+ * of BDRV_WANT_ALLOCATED, BDRV_WANT_OFFSET_VALID, or
|
||||
+ * BDRV_WANT_ZERO based on what the caller is looking for in the
|
||||
+ * results. The block layer guarantees input clamped to
|
||||
+ * bdrv_getlength() and aligned to request_alignment, as well as
|
||||
+ * non-NULL pnum, map, and file; in turn, the driver must return
|
||||
+ * an error or set pnum to an aligned non-zero value.
|
||||
*
|
||||
* Note that @bytes is just a hint on how big of a region the
|
||||
* caller wants to inspect. It is not a limit on *pnum.
|
||||
@@ -628,8 +629,8 @@ struct BlockDriver {
|
||||
* to clamping *pnum for return to its caller.
|
||||
*/
|
||||
int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_block_status)(
|
||||
- BlockDriverState *bs,
|
||||
- bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum,
|
||||
+ BlockDriverState *bs, unsigned int mode,
|
||||
+ int64_t offset, int64_t bytes, int64_t *pnum,
|
||||
int64_t *map, BlockDriverState **file);
|
||||
|
||||
/*
|
||||
@@ -653,8 +654,8 @@ struct BlockDriver {
|
||||
QEMUIOVector *qiov, size_t qiov_offset);
|
||||
|
||||
int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_snapshot_block_status)(
|
||||
- BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes,
|
||||
- int64_t *pnum, int64_t *map, BlockDriverState **file);
|
||||
+ BlockDriverState *bs, unsigned int mode, int64_t offset,
|
||||
+ int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file);
|
||||
|
||||
int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard_snapshot)(
|
||||
BlockDriverState *bs, int64_t offset, int64_t bytes);
|
||||
diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h
|
||||
index 4a7cf2b4fd..4f94eb3c5a 100644
|
||||
--- a/include/block/block_int-io.h
|
||||
+++ b/include/block/block_int-io.h
|
||||
@@ -38,8 +38,8 @@
|
||||
int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_snapshot(BdrvChild *child,
|
||||
int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset);
|
||||
int coroutine_fn GRAPH_RDLOCK bdrv_co_snapshot_block_status(
|
||||
- BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes,
|
||||
- int64_t *pnum, int64_t *map, BlockDriverState **file);
|
||||
+ BlockDriverState *bs, unsigned int mode, int64_t offset,
|
||||
+ int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file);
|
||||
int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard_snapshot(BlockDriverState *bs,
|
||||
int64_t offset, int64_t bytes);
|
||||
|
||||
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
|
||||
index 2b358eaaa8..e26b3be593 100644
|
||||
--- a/tests/unit/test-block-iothread.c
|
||||
+++ b/tests/unit/test-block-iothread.c
|
||||
@@ -63,7 +63,7 @@ bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs,
|
||||
- bool want_zero,
|
||||
+ unsigned int mode,
|
||||
int64_t offset, int64_t count,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,90 @@
|
||||
From e101b9872f9b3f6c5e128f29d7c3bb91faca362b Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
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 <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
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 <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
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 <eblake@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -0,0 +1,64 @@
|
||||
From f8d89f67817fa362a3b8ed0721775e353dac8f18 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:19 -0500
|
||||
Subject: [PATCH 02/14] file-posix, gluster: Handle zero block status hint
|
||||
better
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [2/14] c40cd3f8cda2ea1646d90fd174b5f0dbd3e1a50b (ebblake/centos-qemu-kvm)
|
||||
|
||||
Although the previous patch to change 'bool want_zero' into a bitmask
|
||||
made no semantic change, it is now time to differentiate. When the
|
||||
caller specifically wants to know what parts of the file read as zero,
|
||||
we need to use lseek and actually reporting holes, rather than
|
||||
short-circuiting and advertising full allocation.
|
||||
|
||||
This change will be utilized in later patches to let mirroring
|
||||
optimize for the case when the destination already reads as zeroes.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-17-eblake@redhat.com>
|
||||
(cherry picked from commit a6a0a7fb0e327d17594c971b4a39de14e025b415)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/file-posix.c | 3 ++-
|
||||
block/gluster.c | 2 +-
|
||||
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/block/file-posix.c b/block/file-posix.c
|
||||
index 0c6569742f..dea7b09b6c 100644
|
||||
--- a/block/file-posix.c
|
||||
+++ b/block/file-posix.c
|
||||
@@ -3282,7 +3282,8 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (mode != BDRV_WANT_PRECISE) {
|
||||
+ if (!(mode & BDRV_WANT_ZERO)) {
|
||||
+ /* There is no backing file - all bytes are allocated in this file. */
|
||||
*pnum = bytes;
|
||||
*map = offset;
|
||||
*file = bs;
|
||||
diff --git a/block/gluster.c b/block/gluster.c
|
||||
index 8197b0ecef..e702666cbc 100644
|
||||
--- a/block/gluster.c
|
||||
+++ b/block/gluster.c
|
||||
@@ -1482,7 +1482,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (mode != BDRV_WANT_PRECISE) {
|
||||
+ if (!(mode & BDRV_WANT_ZERO)) {
|
||||
*pnum = bytes;
|
||||
*map = offset;
|
||||
*file = bs;
|
||||
--
|
||||
2.39.3
|
||||
|
42
kvm-iotests-Improve-iotest-194-to-mirror-data.patch
Normal file
42
kvm-iotests-Improve-iotest-194-to-mirror-data.patch
Normal file
@ -0,0 +1,42 @@
|
||||
From 11b46a271d73631177f59ff581a408f967c30fb9 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:22 -0500
|
||||
Subject: [PATCH 05/14] iotests: Improve iotest 194 to mirror data
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [5/14] 9f1fd3c7d4332ac310af4eb37e8f2122f6324294 (ebblake/centos-qemu-kvm)
|
||||
|
||||
Mirroring a completely sparse image to a sparse destination should be
|
||||
practically instantaneous. It isn't yet, but the test will be more
|
||||
realistic if it has some non-zero to mirror as well as the holes.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-20-eblake@redhat.com>
|
||||
(cherry picked from commit eb89627899bb84148d272394e885725eff456ae9)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
tests/qemu-iotests/194 | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194
|
||||
index c0ce82dd25..d0b9c084f5 100755
|
||||
--- a/tests/qemu-iotests/194
|
||||
+++ b/tests/qemu-iotests/194
|
||||
@@ -34,6 +34,7 @@ with iotests.FilePath('source.img') as source_img_path, \
|
||||
|
||||
img_size = '1G'
|
||||
iotests.qemu_img_create('-f', iotests.imgfmt, source_img_path, img_size)
|
||||
+ iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 512M 1M', source_img_path)
|
||||
iotests.qemu_img_create('-f', iotests.imgfmt, dest_img_path, img_size)
|
||||
|
||||
iotests.log('Launching VMs...')
|
||||
--
|
||||
2.39.3
|
||||
|
68
kvm-iotests-common.rc-add-disk_usage-function.patch
Normal file
68
kvm-iotests-common.rc-add-disk_usage-function.patch
Normal file
@ -0,0 +1,68 @@
|
||||
From d8ed5039981b1eb81d229d8ee672d5ee28862e92 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
|
||||
Date: Fri, 9 May 2025 15:40:29 -0500
|
||||
Subject: [PATCH 12/14] iotests/common.rc: add disk_usage function
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [12/14] 0a007f9d09f01b50cf4edeb8ac8217356b2cb5d2 (ebblake/centos-qemu-kvm)
|
||||
|
||||
Move the definition from iotests/250 to common.rc. This is used to
|
||||
detect real disk usage of sparse files. In particular, we want to use
|
||||
it for checking subclusters-based discards.
|
||||
|
||||
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
|
||||
Reviewed-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com>
|
||||
Reviewed-by: Alberto Garcia <berto@igalia.com>
|
||||
Message-ID: <20240913163942.423050-6-andrey.drobyshev@virtuozzo.com>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-27-eblake@redhat.com>
|
||||
(cherry picked from commit be9bac072ede6e6aa27079f59efcf17b56bd7b26)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
tests/qemu-iotests/250 | 5 -----
|
||||
tests/qemu-iotests/common.rc | 6 ++++++
|
||||
2 files changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/tests/qemu-iotests/250 b/tests/qemu-iotests/250
|
||||
index af48f83aba..c0a0dbc0ff 100755
|
||||
--- a/tests/qemu-iotests/250
|
||||
+++ b/tests/qemu-iotests/250
|
||||
@@ -52,11 +52,6 @@ _unsupported_imgopts data_file
|
||||
# bdrv_co_truncate(bs->file) call in qcow2_co_truncate(), which might succeed
|
||||
# anyway.
|
||||
|
||||
-disk_usage()
|
||||
-{
|
||||
- du --block-size=1 $1 | awk '{print $1}'
|
||||
-}
|
||||
-
|
||||
size=2100M
|
||||
|
||||
_make_test_img -o "cluster_size=1M,preallocation=metadata" $size
|
||||
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
|
||||
index 95c12577dd..237f746af8 100644
|
||||
--- a/tests/qemu-iotests/common.rc
|
||||
+++ b/tests/qemu-iotests/common.rc
|
||||
@@ -140,6 +140,12 @@ _optstr_add()
|
||||
fi
|
||||
}
|
||||
|
||||
+# report real disk usage for sparse files
|
||||
+disk_usage()
|
||||
+{
|
||||
+ du --block-size=1 "$1" | awk '{print $1}'
|
||||
+}
|
||||
+
|
||||
# Set the variables to the empty string to turn Valgrind off
|
||||
# for specific processes, e.g.
|
||||
# $ VALGRIND_QEMU_IO= ./check -qcow2 -valgrind 015
|
||||
--
|
||||
2.39.3
|
||||
|
295
kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch
Normal file
295
kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch
Normal file
@ -0,0 +1,295 @@
|
||||
From bc4571743fc3bbb829101fbf294615e3b8fb3577 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:25 -0500
|
||||
Subject: [PATCH 08/14] mirror: Allow QMP override to declare target already
|
||||
zero
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [8/14] 4ad06d67db8c43df4e6e0b8f929b1d8c19e4b338 (ebblake/centos-qemu-kvm)
|
||||
|
||||
QEMU has an optimization for a just-created drive-mirror destination
|
||||
that is not possible for blockdev-mirror (which can't create the
|
||||
destination) - any time we know the destination starts life as all
|
||||
zeroes, we can skip a pre-zeroing pass on the destination. Recent
|
||||
patches have added an improved heuristic for detecting if a file
|
||||
contains all zeroes, and we plan to use that heuristic in upcoming
|
||||
patches. But since a heuristic cannot quickly detect all scenarios,
|
||||
and there may be cases where the caller is aware of information that
|
||||
QEMU cannot learn quickly, it makes sense to have a way to tell QEMU
|
||||
to assume facts about the destination that can make the mirror
|
||||
operation faster. Given our existing example of "qemu-img convert
|
||||
--target-is-zero", it is time to expose this override in QMP for
|
||||
blockdev-mirror as well.
|
||||
|
||||
This patch results in some slight redundancy between the older
|
||||
s->zero_target (set any time mode==FULL and the destination image was
|
||||
not just created - ie. clear if drive-mirror is asking to skip the
|
||||
pre-zero pass) and the newly-introduced s->target_is_zero (in addition
|
||||
to the QMP override, it is set when drive-mirror creates the
|
||||
destination image); this will be cleaned up in the next patch.
|
||||
|
||||
There is also a subtlety that we must consider. When drive-mirror is
|
||||
passing target_is_zero on behalf of a just-created image, we know the
|
||||
image is sparse (skipping the pre-zeroing keeps it that way), so it
|
||||
doesn't matter whether the destination also has "discard":"unmap" and
|
||||
"detect-zeroes":"unmap". But now that we are letting the user set the
|
||||
knob for target-is-zero, if the user passes a pre-existing file that
|
||||
is fully allocated, it is fine to leave the file fully allocated under
|
||||
"detect-zeroes":"on", but if the file is open with
|
||||
"detect-zeroes":"unmap", we should really be trying harder to punch
|
||||
holes in the destination for every region of zeroes copied from the
|
||||
source. The easiest way to do this is to still run the pre-zeroing
|
||||
pass (turning the entire destination file sparse before populating
|
||||
just the allocated portions of the source), even though that currently
|
||||
results in double I/O to the portions of the file that are allocated.
|
||||
A later patch will add further optimizations to reduce redundant
|
||||
zeroing I/O during the mirror operation.
|
||||
|
||||
Since "target-is-zero":true is designed for optimizations, it is okay
|
||||
to silently ignore the parameter rather than erroring if the user ever
|
||||
sets the parameter in a scenario where the mirror job can't exploit it
|
||||
(for example, when doing "sync":"top" instead of "sync":"full", we
|
||||
can't pre-zero, so setting the parameter won't make a speed
|
||||
difference).
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Acked-by: Markus Armbruster <armbru@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-23-eblake@redhat.com>
|
||||
Reviewed-by: Sunny Zhu <sunnyzhyy@qq.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
(cherry picked from commit d17a34bfb94bda3a89d7320ae67255ded1d8c939)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/mirror.c | 27 ++++++++++++++++++++++----
|
||||
blockdev.c | 18 ++++++++++-------
|
||||
include/block/block_int-global-state.h | 3 ++-
|
||||
qapi/block-core.json | 8 +++++++-
|
||||
tests/unit/test-block-iothread.c | 2 +-
|
||||
5 files changed, 44 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/block/mirror.c b/block/mirror.c
|
||||
index 2599b75d09..4dcb50c81a 100644
|
||||
--- a/block/mirror.c
|
||||
+++ b/block/mirror.c
|
||||
@@ -55,6 +55,8 @@ typedef struct MirrorBlockJob {
|
||||
BlockMirrorBackingMode backing_mode;
|
||||
/* Whether the target image requires explicit zero-initialization */
|
||||
bool zero_target;
|
||||
+ /* Whether the target should be assumed to be already zero initialized */
|
||||
+ bool target_is_zero;
|
||||
/*
|
||||
* To be accesssed with atomics. Written only under the BQL (required by the
|
||||
* current implementation of mirror_change()).
|
||||
@@ -844,12 +846,26 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
BlockDriverState *target_bs = blk_bs(s->target);
|
||||
int ret = -EIO;
|
||||
int64_t count;
|
||||
+ bool punch_holes =
|
||||
+ target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
|
||||
+ bdrv_can_write_zeroes_with_unmap(target_bs);
|
||||
|
||||
bdrv_graph_co_rdlock();
|
||||
bs = s->mirror_top_bs->backing->bs;
|
||||
bdrv_graph_co_rdunlock();
|
||||
|
||||
- if (s->zero_target) {
|
||||
+ if (s->zero_target && (!s->target_is_zero || punch_holes)) {
|
||||
+ /*
|
||||
+ * Here, we are in FULL mode; our goal is to avoid writing
|
||||
+ * zeroes if the destination already reads as zero, except
|
||||
+ * when we are trying to punch holes. This is possible if
|
||||
+ * zeroing happened externally (s->target_is_zero) or if we
|
||||
+ * have a fast way to pre-zero the image (the dirty bitmap
|
||||
+ * will be populated later by the non-zero portions, the same
|
||||
+ * as for TOP mode). If pre-zeroing is not fast, or we need
|
||||
+ * to punch holes, then our only recourse is to write the
|
||||
+ * entire image.
|
||||
+ */
|
||||
if (!bdrv_can_write_zeroes_with_unmap(target_bs)) {
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length);
|
||||
return 0;
|
||||
@@ -1714,7 +1730,7 @@ static BlockJob *mirror_start_job(
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode sync_mode,
|
||||
BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target,
|
||||
+ bool zero_target, bool target_is_zero,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap,
|
||||
@@ -1883,6 +1899,7 @@ static BlockJob *mirror_start_job(
|
||||
s->sync_mode = sync_mode;
|
||||
s->backing_mode = backing_mode;
|
||||
s->zero_target = zero_target;
|
||||
+ s->target_is_zero = target_is_zero;
|
||||
qatomic_set(&s->copy_mode, copy_mode);
|
||||
s->base = base;
|
||||
s->base_overlay = bdrv_find_overlay(bs, base);
|
||||
@@ -2011,7 +2028,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
int creation_flags, int64_t speed,
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target,
|
||||
+ bool zero_target, bool target_is_zero,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap, const char *filter_node_name,
|
||||
@@ -2034,7 +2051,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
|
||||
mirror_start_job(job_id, bs, creation_flags, target, replaces,
|
||||
speed, granularity, buf_size, mode, backing_mode,
|
||||
- zero_target, on_source_error, on_target_error, unmap,
|
||||
+ zero_target,
|
||||
+ target_is_zero, on_source_error, on_target_error, unmap,
|
||||
NULL, NULL, &mirror_job_driver, base, false,
|
||||
filter_node_name, true, copy_mode, false, errp);
|
||||
}
|
||||
@@ -2062,6 +2080,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
job = mirror_start_job(
|
||||
job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
||||
MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false,
|
||||
+ false,
|
||||
on_error, on_error, true, cb, opaque,
|
||||
&commit_active_job_driver, base, auto_complete,
|
||||
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
|
||||
diff --git a/blockdev.c b/blockdev.c
|
||||
index 1d1f27cfff..2e2fed539e 100644
|
||||
--- a/blockdev.c
|
||||
+++ b/blockdev.c
|
||||
@@ -2798,7 +2798,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target,
|
||||
+ bool zero_target, bool target_is_zero,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
@@ -2909,11 +2909,10 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
/* pass the node name to replace to mirror start since it's loose coupling
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
- mirror_start(job_id, bs, target,
|
||||
- replaces, job_flags,
|
||||
+ mirror_start(job_id, bs, target, replaces, job_flags,
|
||||
speed, granularity, buf_size, sync, backing_mode, zero_target,
|
||||
- on_source_error, on_target_error, unmap, filter_node_name,
|
||||
- copy_mode, errp);
|
||||
+ target_is_zero, on_source_error, on_target_error, unmap,
|
||||
+ filter_node_name, copy_mode, errp);
|
||||
}
|
||||
|
||||
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
@@ -2928,6 +2927,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
int64_t size;
|
||||
const char *format = arg->format;
|
||||
bool zero_target;
|
||||
+ bool target_is_zero;
|
||||
int ret;
|
||||
|
||||
bs = qmp_get_root_bs(arg->device, errp);
|
||||
@@ -3044,6 +3044,8 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL &&
|
||||
(arg->mode == NEW_IMAGE_MODE_EXISTING ||
|
||||
!bdrv_has_zero_init(target_bs)));
|
||||
+ target_is_zero = (arg->mode != NEW_IMAGE_MODE_EXISTING &&
|
||||
+ bdrv_has_zero_init(target_bs));
|
||||
bdrv_graph_rdunlock_main_loop();
|
||||
|
||||
|
||||
@@ -3055,7 +3057,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
|
||||
blockdev_mirror_common(arg->job_id, bs, target_bs,
|
||||
arg->replaces, arg->sync,
|
||||
- backing_mode, zero_target,
|
||||
+ backing_mode, zero_target, target_is_zero,
|
||||
arg->has_speed, arg->speed,
|
||||
arg->has_granularity, arg->granularity,
|
||||
arg->has_buf_size, arg->buf_size,
|
||||
@@ -3085,6 +3087,7 @@ void qmp_blockdev_mirror(const char *job_id,
|
||||
bool has_copy_mode, MirrorCopyMode copy_mode,
|
||||
bool has_auto_finalize, bool auto_finalize,
|
||||
bool has_auto_dismiss, bool auto_dismiss,
|
||||
+ bool has_target_is_zero, bool target_is_zero,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -3115,7 +3118,8 @@ void qmp_blockdev_mirror(const char *job_id,
|
||||
|
||||
blockdev_mirror_common(job_id, bs, target_bs,
|
||||
replaces, sync, backing_mode,
|
||||
- zero_target, has_speed, speed,
|
||||
+ zero_target, has_target_is_zero && target_is_zero,
|
||||
+ has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
has_buf_size, buf_size,
|
||||
has_on_source_error, on_source_error,
|
||||
diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h
|
||||
index eb2d92a226..8cf0003ce7 100644
|
||||
--- a/include/block/block_int-global-state.h
|
||||
+++ b/include/block/block_int-global-state.h
|
||||
@@ -140,6 +140,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
* @mode: Whether to collapse all images in the chain to the target.
|
||||
* @backing_mode: How to establish the target's backing chain after completion.
|
||||
* @zero_target: Whether the target should be explicitly zero-initialized
|
||||
+ * @target_is_zero: Whether the target already is zero-initialized.
|
||||
* @on_source_error: The action to take upon error reading from the source.
|
||||
* @on_target_error: The action to take upon error writing to the target.
|
||||
* @unmap: Whether to unmap target where source sectors only contain zeroes.
|
||||
@@ -159,7 +160,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
int creation_flags, int64_t speed,
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target,
|
||||
+ bool zero_target, bool target_is_zero,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap, const char *filter_node_name,
|
||||
diff --git a/qapi/block-core.json b/qapi/block-core.json
|
||||
index b1937780e1..7f70ec6d3c 100644
|
||||
--- a/qapi/block-core.json
|
||||
+++ b/qapi/block-core.json
|
||||
@@ -2538,6 +2538,11 @@
|
||||
# disappear from the query list without user intervention.
|
||||
# Defaults to true. (Since 3.1)
|
||||
#
|
||||
+# @target-is-zero: Assume the destination reads as all zeroes before
|
||||
+# the mirror started. Setting this to true can speed up the
|
||||
+# mirror. Setting this to true when the destination is not
|
||||
+# actually all zero can corrupt the destination. (Since 10.1)
|
||||
+#
|
||||
# Since: 2.6
|
||||
#
|
||||
# .. qmp-example::
|
||||
@@ -2557,7 +2562,8 @@
|
||||
'*on-target-error': 'BlockdevOnError',
|
||||
'*filter-node-name': 'str',
|
||||
'*copy-mode': 'MirrorCopyMode',
|
||||
- '*auto-finalize': 'bool', '*auto-dismiss': 'bool' },
|
||||
+ '*auto-finalize': 'bool', '*auto-dismiss': 'bool',
|
||||
+ '*target-is-zero': 'bool'},
|
||||
'allow-preconfig': true }
|
||||
|
||||
##
|
||||
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
|
||||
index e26b3be593..54aed8252c 100644
|
||||
--- a/tests/unit/test-block-iothread.c
|
||||
+++ b/tests/unit/test-block-iothread.c
|
||||
@@ -755,7 +755,7 @@ static void test_propagate_mirror(void)
|
||||
|
||||
/* Start a mirror job */
|
||||
mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0,
|
||||
- MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false,
|
||||
+ MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, false,
|
||||
BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
|
||||
false, "filter_node", MIRROR_COPY_MODE_BACKGROUND,
|
||||
&error_abort);
|
||||
--
|
||||
2.39.3
|
||||
|
241
kvm-mirror-Drop-redundant-zero_target-parameter.patch
Normal file
241
kvm-mirror-Drop-redundant-zero_target-parameter.patch
Normal file
@ -0,0 +1,241 @@
|
||||
From db1a158312c2b94af1c1a50e0f13ace6ae58f0b6 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:26 -0500
|
||||
Subject: [PATCH 09/14] mirror: Drop redundant zero_target parameter
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [9/14] b4cbd267c81b4758f59e0d51b947fd450caf6ef5 (ebblake/centos-qemu-kvm)
|
||||
|
||||
The two callers to a mirror job (drive-mirror and blockdev-mirror) set
|
||||
zero_target precisely when sync mode == FULL, with the one exception
|
||||
that drive-mirror skips zeroing the target if it was newly created and
|
||||
reads as zero. But given the previous patch, that exception is
|
||||
equally captured by target_is_zero.
|
||||
|
||||
Meanwhile, there is another slight wrinkle, fortunately caught by
|
||||
iotest 185: if the caller uses "sync":"top" but the source has no
|
||||
backing file, the code in blockdev.c was changing sync to be FULL, but
|
||||
only after it had set zero_target=false. In mirror.c, prior to recent
|
||||
patches, this didn't matter: the only places that inspected sync were
|
||||
setting is_none_mode (both TOP and FULL had set that to false), and
|
||||
mirror_start() setting base = mode == MIRROR_SYNC_MODE_TOP ?
|
||||
bdrv_backing_chain_next(bs) : NULL. But now that we are passing sync
|
||||
around, the slammed sync mode would result in a new pre-zeroing pass
|
||||
even when the user had passed "sync":"top" in an effort to skip
|
||||
pre-zeroing. Fortunately, the assignment of base when bs has no
|
||||
backing chain still works out to NULL if we don't slam things. So
|
||||
with the forced change of sync ripped out of blockdev.c, the sync mode
|
||||
is passed through the full callstack unmolested, and we can now
|
||||
reliably reconstruct the same settings as what used to be passed in by
|
||||
zero_target=false, without the redundant parameter.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-24-eblake@redhat.com>
|
||||
Reviewed-by: Sunny Zhu <sunnyzhyy@qq.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
[eblake: Fix regression in iotest 185]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
(cherry picked from commit 253b43a29077de9266351e120c600a73b82e9c49)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/mirror.c | 13 +++++--------
|
||||
blockdev.c | 19 ++++---------------
|
||||
include/block/block_int-global-state.h | 3 +--
|
||||
tests/unit/test-block-iothread.c | 2 +-
|
||||
4 files changed, 11 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/block/mirror.c b/block/mirror.c
|
||||
index 4dcb50c81a..d04db85883 100644
|
||||
--- a/block/mirror.c
|
||||
+++ b/block/mirror.c
|
||||
@@ -53,8 +53,6 @@ typedef struct MirrorBlockJob {
|
||||
Error *replace_blocker;
|
||||
MirrorSyncMode sync_mode;
|
||||
BlockMirrorBackingMode backing_mode;
|
||||
- /* Whether the target image requires explicit zero-initialization */
|
||||
- bool zero_target;
|
||||
/* Whether the target should be assumed to be already zero initialized */
|
||||
bool target_is_zero;
|
||||
/*
|
||||
@@ -854,7 +852,9 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
bs = s->mirror_top_bs->backing->bs;
|
||||
bdrv_graph_co_rdunlock();
|
||||
|
||||
- if (s->zero_target && (!s->target_is_zero || punch_holes)) {
|
||||
+ if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
+ /* In TOP mode, there is no benefit to a pre-zeroing pass. */
|
||||
+ } else if (!s->target_is_zero || punch_holes) {
|
||||
/*
|
||||
* Here, we are in FULL mode; our goal is to avoid writing
|
||||
* zeroes if the destination already reads as zero, except
|
||||
@@ -1730,7 +1730,7 @@ static BlockJob *mirror_start_job(
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode sync_mode,
|
||||
BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target, bool target_is_zero,
|
||||
+ bool target_is_zero,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap,
|
||||
@@ -1898,7 +1898,6 @@ static BlockJob *mirror_start_job(
|
||||
s->on_target_error = on_target_error;
|
||||
s->sync_mode = sync_mode;
|
||||
s->backing_mode = backing_mode;
|
||||
- s->zero_target = zero_target;
|
||||
s->target_is_zero = target_is_zero;
|
||||
qatomic_set(&s->copy_mode, copy_mode);
|
||||
s->base = base;
|
||||
@@ -2028,7 +2027,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
int creation_flags, int64_t speed,
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target, bool target_is_zero,
|
||||
+ bool target_is_zero,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap, const char *filter_node_name,
|
||||
@@ -2051,7 +2050,6 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
|
||||
mirror_start_job(job_id, bs, creation_flags, target, replaces,
|
||||
speed, granularity, buf_size, mode, backing_mode,
|
||||
- zero_target,
|
||||
target_is_zero, on_source_error, on_target_error, unmap,
|
||||
NULL, NULL, &mirror_job_driver, base, false,
|
||||
filter_node_name, true, copy_mode, false, errp);
|
||||
@@ -2080,7 +2078,6 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
job = mirror_start_job(
|
||||
job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
||||
MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false,
|
||||
- false,
|
||||
on_error, on_error, true, cb, opaque,
|
||||
&commit_active_job_driver, base, auto_complete,
|
||||
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
|
||||
diff --git a/blockdev.c b/blockdev.c
|
||||
index 2e2fed539e..0fa8813efe 100644
|
||||
--- a/blockdev.c
|
||||
+++ b/blockdev.c
|
||||
@@ -2798,7 +2798,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target, bool target_is_zero,
|
||||
+ bool target_is_zero,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
@@ -2865,10 +2865,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
return;
|
||||
}
|
||||
|
||||
- if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) {
|
||||
- sync = MIRROR_SYNC_MODE_FULL;
|
||||
- }
|
||||
-
|
||||
if (!replaces) {
|
||||
/* We want to mirror from @bs, but keep implicit filters on top */
|
||||
unfiltered_bs = bdrv_skip_implicit_filters(bs);
|
||||
@@ -2910,7 +2906,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
mirror_start(job_id, bs, target, replaces, job_flags,
|
||||
- speed, granularity, buf_size, sync, backing_mode, zero_target,
|
||||
+ speed, granularity, buf_size, sync, backing_mode,
|
||||
target_is_zero, on_source_error, on_target_error, unmap,
|
||||
filter_node_name, copy_mode, errp);
|
||||
}
|
||||
@@ -2926,7 +2922,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
int flags;
|
||||
int64_t size;
|
||||
const char *format = arg->format;
|
||||
- bool zero_target;
|
||||
bool target_is_zero;
|
||||
int ret;
|
||||
|
||||
@@ -3041,9 +3036,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
}
|
||||
|
||||
bdrv_graph_rdlock_main_loop();
|
||||
- zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL &&
|
||||
- (arg->mode == NEW_IMAGE_MODE_EXISTING ||
|
||||
- !bdrv_has_zero_init(target_bs)));
|
||||
target_is_zero = (arg->mode != NEW_IMAGE_MODE_EXISTING &&
|
||||
bdrv_has_zero_init(target_bs));
|
||||
bdrv_graph_rdunlock_main_loop();
|
||||
@@ -3057,7 +3049,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
|
||||
blockdev_mirror_common(arg->job_id, bs, target_bs,
|
||||
arg->replaces, arg->sync,
|
||||
- backing_mode, zero_target, target_is_zero,
|
||||
+ backing_mode, target_is_zero,
|
||||
arg->has_speed, arg->speed,
|
||||
arg->has_granularity, arg->granularity,
|
||||
arg->has_buf_size, arg->buf_size,
|
||||
@@ -3094,7 +3086,6 @@ void qmp_blockdev_mirror(const char *job_id,
|
||||
BlockDriverState *target_bs;
|
||||
AioContext *aio_context;
|
||||
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
|
||||
- bool zero_target;
|
||||
int ret;
|
||||
|
||||
bs = qmp_get_root_bs(device, errp);
|
||||
@@ -3107,8 +3098,6 @@ void qmp_blockdev_mirror(const char *job_id,
|
||||
return;
|
||||
}
|
||||
|
||||
- zero_target = (sync == MIRROR_SYNC_MODE_FULL);
|
||||
-
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp);
|
||||
@@ -3118,7 +3107,7 @@ void qmp_blockdev_mirror(const char *job_id,
|
||||
|
||||
blockdev_mirror_common(job_id, bs, target_bs,
|
||||
replaces, sync, backing_mode,
|
||||
- zero_target, has_target_is_zero && target_is_zero,
|
||||
+ has_target_is_zero && target_is_zero,
|
||||
has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
has_buf_size, buf_size,
|
||||
diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h
|
||||
index 8cf0003ce7..d21bd7fd2f 100644
|
||||
--- a/include/block/block_int-global-state.h
|
||||
+++ b/include/block/block_int-global-state.h
|
||||
@@ -139,7 +139,6 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
* @buf_size: The amount of data that can be in flight at one time.
|
||||
* @mode: Whether to collapse all images in the chain to the target.
|
||||
* @backing_mode: How to establish the target's backing chain after completion.
|
||||
- * @zero_target: Whether the target should be explicitly zero-initialized
|
||||
* @target_is_zero: Whether the target already is zero-initialized.
|
||||
* @on_source_error: The action to take upon error reading from the source.
|
||||
* @on_target_error: The action to take upon error writing to the target.
|
||||
@@ -160,7 +159,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
int creation_flags, int64_t speed,
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
- bool zero_target, bool target_is_zero,
|
||||
+ bool target_is_zero,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap, const char *filter_node_name,
|
||||
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
|
||||
index 54aed8252c..e26b3be593 100644
|
||||
--- a/tests/unit/test-block-iothread.c
|
||||
+++ b/tests/unit/test-block-iothread.c
|
||||
@@ -755,7 +755,7 @@ static void test_propagate_mirror(void)
|
||||
|
||||
/* Start a mirror job */
|
||||
mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0,
|
||||
- MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, false,
|
||||
+ MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false,
|
||||
BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
|
||||
false, "filter_node", MIRROR_COPY_MODE_BACKGROUND,
|
||||
&error_abort);
|
||||
--
|
||||
2.39.3
|
||||
|
90
kvm-mirror-Minor-refactoring.patch
Normal file
90
kvm-mirror-Minor-refactoring.patch
Normal file
@ -0,0 +1,90 @@
|
||||
From e95294aecc606deacf716861d716f9178b132ed8 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:23 -0500
|
||||
Subject: [PATCH 06/14] mirror: Minor refactoring
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [6/14] 22a87aca8033f3f5d10fd224dc0786633a4d040f (ebblake/centos-qemu-kvm)
|
||||
|
||||
Commit 5791ba52 (v9.2) pre-initialized ret in mirror_dirty_init to
|
||||
silence a false positive compiler warning, even though in all code
|
||||
paths where ret is used, it was guaranteed to be reassigned
|
||||
beforehand. But since the function returns -errno, and -1 is not
|
||||
always the right errno, it's better to initialize to -EIO.
|
||||
|
||||
An upcoming patch wants to track two bitmaps in
|
||||
do_sync_target_write(); this will be easier if the current variables
|
||||
related to the dirty bitmap are renamed.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-21-eblake@redhat.com>
|
||||
(cherry picked from commit 870f8963cf1a84f8ec929b05a6d68906974a76c5)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/mirror.c | 22 +++++++++++-----------
|
||||
1 file changed, 11 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/block/mirror.c b/block/mirror.c
|
||||
index a53582f17b..34c6c5252e 100644
|
||||
--- a/block/mirror.c
|
||||
+++ b/block/mirror.c
|
||||
@@ -841,7 +841,7 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
int64_t offset;
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *target_bs = blk_bs(s->target);
|
||||
- int ret = -1;
|
||||
+ int ret = -EIO;
|
||||
int64_t count;
|
||||
|
||||
bdrv_graph_co_rdlock();
|
||||
@@ -1341,7 +1341,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||
{
|
||||
int ret;
|
||||
size_t qiov_offset = 0;
|
||||
- int64_t bitmap_offset, bitmap_end;
|
||||
+ int64_t dirty_bitmap_offset, dirty_bitmap_end;
|
||||
|
||||
if (!QEMU_IS_ALIGNED(offset, job->granularity) &&
|
||||
bdrv_dirty_bitmap_get(job->dirty_bitmap, offset))
|
||||
@@ -1388,11 +1388,11 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||
* Tails are either clean or shrunk, so for bitmap resetting
|
||||
* we safely align the range down.
|
||||
*/
|
||||
- bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity);
|
||||
- bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity);
|
||||
- if (bitmap_offset < bitmap_end) {
|
||||
- bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset,
|
||||
- bitmap_end - bitmap_offset);
|
||||
+ dirty_bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity);
|
||||
+ dirty_bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity);
|
||||
+ if (dirty_bitmap_offset < dirty_bitmap_end) {
|
||||
+ bdrv_reset_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset,
|
||||
+ dirty_bitmap_end - dirty_bitmap_offset);
|
||||
}
|
||||
|
||||
job_progress_increase_remaining(&job->common.job, bytes);
|
||||
@@ -1430,10 +1430,10 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||
* at function start, and they must be still dirty, as we've locked
|
||||
* the region for in-flight op.
|
||||
*/
|
||||
- bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity);
|
||||
- bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity);
|
||||
- bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset,
|
||||
- bitmap_end - bitmap_offset);
|
||||
+ dirty_bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity);
|
||||
+ dirty_bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity);
|
||||
+ bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset,
|
||||
+ dirty_bitmap_end - dirty_bitmap_offset);
|
||||
qatomic_set(&job->actively_synced, false);
|
||||
|
||||
action = mirror_error_action(job, false, -ret);
|
||||
--
|
||||
2.39.3
|
||||
|
139
kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch
Normal file
139
kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch
Normal file
@ -0,0 +1,139 @@
|
||||
From db0b92495a4e774caafaaa148e778b575112bad2 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:24 -0500
|
||||
Subject: [PATCH 07/14] mirror: Pass full sync mode rather than bool to
|
||||
internals
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [7/14] e8872e3edad069ee6c76f6104c1bc277c025b5ac (ebblake/centos-qemu-kvm)
|
||||
|
||||
Out of the five possible values for MirrorSyncMode, INCREMENTAL and
|
||||
BITMAP are already rejected up front in mirror_start, leaving NONE,
|
||||
TOP, and FULL as the remaining values that the code was collapsing
|
||||
into a single bool is_none_mode. Furthermore, mirror_dirty_init() is
|
||||
only reachable for modes TOP and FULL, as further guided by
|
||||
s->zero_target. However, upcoming patches want to further optimize
|
||||
the pre-zeroing pass of a sync=full mirror in mirror_dirty_init(),
|
||||
while avoiding that pass on a sync=top action. Instead of throwing
|
||||
away context by collapsing these two values into
|
||||
s->is_none_mode=false, it is better to pass s->sync_mode throughout
|
||||
the entire operation. For active commit, the desired semantics match
|
||||
sync mode TOP.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-22-eblake@redhat.com>
|
||||
Reviewed-by: Sunny Zhu <sunnyzhyy@qq.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
(cherry picked from commit 9474d97bd7421b4fe7c806ab0949697514d11e88)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/mirror.c | 24 ++++++++++++------------
|
||||
1 file changed, 12 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/block/mirror.c b/block/mirror.c
|
||||
index 34c6c5252e..2599b75d09 100644
|
||||
--- a/block/mirror.c
|
||||
+++ b/block/mirror.c
|
||||
@@ -51,7 +51,7 @@ typedef struct MirrorBlockJob {
|
||||
BlockDriverState *to_replace;
|
||||
/* Used to block operations on the drive-mirror-replace target */
|
||||
Error *replace_blocker;
|
||||
- bool is_none_mode;
|
||||
+ MirrorSyncMode sync_mode;
|
||||
BlockMirrorBackingMode backing_mode;
|
||||
/* Whether the target image requires explicit zero-initialization */
|
||||
bool zero_target;
|
||||
@@ -723,9 +723,10 @@ static int mirror_exit_common(Job *job)
|
||||
&error_abort);
|
||||
|
||||
if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
|
||||
- BlockDriverState *backing = s->is_none_mode ? src : s->base;
|
||||
+ BlockDriverState *backing;
|
||||
BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs);
|
||||
|
||||
+ backing = s->sync_mode == MIRROR_SYNC_MODE_NONE ? src : s->base;
|
||||
if (bdrv_cow_bs(unfiltered_target) != backing) {
|
||||
bdrv_set_backing_hd(unfiltered_target, backing, &local_err);
|
||||
if (local_err) {
|
||||
@@ -1020,7 +1021,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
|
||||
mirror_free_init(s);
|
||||
|
||||
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
- if (!s->is_none_mode) {
|
||||
+ if (s->sync_mode != MIRROR_SYNC_MODE_NONE) {
|
||||
ret = mirror_dirty_init(s);
|
||||
if (ret < 0 || job_is_cancelled(&s->common.job)) {
|
||||
goto immediate_exit;
|
||||
@@ -1711,6 +1712,7 @@ static BlockJob *mirror_start_job(
|
||||
int creation_flags, BlockDriverState *target,
|
||||
const char *replaces, int64_t speed,
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
+ MirrorSyncMode sync_mode,
|
||||
BlockMirrorBackingMode backing_mode,
|
||||
bool zero_target,
|
||||
BlockdevOnError on_source_error,
|
||||
@@ -1719,7 +1721,7 @@ static BlockJob *mirror_start_job(
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque,
|
||||
const BlockJobDriver *driver,
|
||||
- bool is_none_mode, BlockDriverState *base,
|
||||
+ BlockDriverState *base,
|
||||
bool auto_complete, const char *filter_node_name,
|
||||
bool is_mirror, MirrorCopyMode copy_mode,
|
||||
bool base_ro,
|
||||
@@ -1878,7 +1880,7 @@ static BlockJob *mirror_start_job(
|
||||
s->replaces = g_strdup(replaces);
|
||||
s->on_source_error = on_source_error;
|
||||
s->on_target_error = on_target_error;
|
||||
- s->is_none_mode = is_none_mode;
|
||||
+ s->sync_mode = sync_mode;
|
||||
s->backing_mode = backing_mode;
|
||||
s->zero_target = zero_target;
|
||||
qatomic_set(&s->copy_mode, copy_mode);
|
||||
@@ -2015,7 +2017,6 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
bool unmap, const char *filter_node_name,
|
||||
MirrorCopyMode copy_mode, Error **errp)
|
||||
{
|
||||
- bool is_none_mode;
|
||||
BlockDriverState *base;
|
||||
|
||||
GLOBAL_STATE_CODE();
|
||||
@@ -2028,14 +2029,13 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
bdrv_graph_rdlock_main_loop();
|
||||
- is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
||||
base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL;
|
||||
bdrv_graph_rdunlock_main_loop();
|
||||
|
||||
mirror_start_job(job_id, bs, creation_flags, target, replaces,
|
||||
- speed, granularity, buf_size, backing_mode, zero_target,
|
||||
- on_source_error, on_target_error, unmap, NULL, NULL,
|
||||
- &mirror_job_driver, is_none_mode, base, false,
|
||||
+ speed, granularity, buf_size, mode, backing_mode,
|
||||
+ zero_target, on_source_error, on_target_error, unmap,
|
||||
+ NULL, NULL, &mirror_job_driver, base, false,
|
||||
filter_node_name, true, copy_mode, false, errp);
|
||||
}
|
||||
|
||||
@@ -2061,9 +2061,9 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
|
||||
job = mirror_start_job(
|
||||
job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
||||
- MIRROR_LEAVE_BACKING_CHAIN, false,
|
||||
+ MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false,
|
||||
on_error, on_error, true, cb, opaque,
|
||||
- &commit_active_job_driver, false, base, auto_complete,
|
||||
+ &commit_active_job_driver, base, auto_complete,
|
||||
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
|
||||
base_read_only, errp);
|
||||
if (!job) {
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,58 @@
|
||||
From 9fedd14da6f1dc7aa3f0711d86f722397d080993 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Tue, 13 May 2025 17:00:45 -0500
|
||||
Subject: [PATCH 14/14] mirror: Reduce I/O when destination is
|
||||
detect-zeroes:unmap
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [14/14] d4ba9d88a8da00f82c2ba7ebf050152fbe1e2465 (ebblake/centos-qemu-kvm)
|
||||
|
||||
If we are going to punch holes in the mirror destination even for the
|
||||
portions where the source image is unallocated, it is nicer to treat
|
||||
the entire image as dirty and punch as we go, rather than pre-zeroing
|
||||
the entire image just to re-do I/O to the allocated portions of the
|
||||
image.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20250513220142.535200-2-eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
(cherry picked from commit 9abfc81246c9cc1845080eec5920779961187c07)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/mirror.c | 13 +++++++++----
|
||||
1 file changed, 9 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/block/mirror.c b/block/mirror.c
|
||||
index 724318f037..c2c5099c95 100644
|
||||
--- a/block/mirror.c
|
||||
+++ b/block/mirror.c
|
||||
@@ -920,11 +920,16 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
* zeroing happened externally (ret > 0) or if we have a fast
|
||||
* way to pre-zero the image (the dirty bitmap will be
|
||||
* populated later by the non-zero portions, the same as for
|
||||
- * TOP mode). If pre-zeroing is not fast, then our only
|
||||
- * recourse is to mark the entire image dirty. The act of
|
||||
- * pre-zeroing will populate the zero bitmap.
|
||||
+ * TOP mode). If pre-zeroing is not fast, or we need to visit
|
||||
+ * the entire image in order to punch holes even in the
|
||||
+ * non-allocated regions of the source, then just mark the
|
||||
+ * entire image dirty and leave the zero bitmap clear at this
|
||||
+ * point in time. Otherwise, it can be faster to pre-zero the
|
||||
+ * image now, even if we re-write the allocated portions of
|
||||
+ * the disk later, and the pre-zero pass will populate the
|
||||
+ * zero bitmap.
|
||||
*/
|
||||
- if (!bdrv_can_write_zeroes_with_unmap(target_bs)) {
|
||||
+ if (!bdrv_can_write_zeroes_with_unmap(target_bs) || punch_holes) {
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length);
|
||||
return 0;
|
||||
}
|
||||
--
|
||||
2.39.3
|
||||
|
180
kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch
Normal file
180
kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch
Normal file
@ -0,0 +1,180 @@
|
||||
From 92a033b6c8394c8efb5b881cbbe463eeff5711cd Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:27 -0500
|
||||
Subject: [PATCH 10/14] mirror: Skip pre-zeroing destination if it is already
|
||||
zero
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [10/14] 5d86d9c763a1bb49fab591a45e211d3be819ccfe (ebblake/centos-qemu-kvm)
|
||||
|
||||
When doing a sync=full mirroring, we can skip pre-zeroing the
|
||||
destination if it already reads as zeroes and we are not also trying
|
||||
to punch holes due to detect-zeroes. With this patch, there are fewer
|
||||
scenarios that have to pass in an explicit target-is-zero, while still
|
||||
resulting in a sparse destination remaining sparse.
|
||||
|
||||
A later patch will then further improve things to skip writing to the
|
||||
destination for parts of the image where the source is zero; but even
|
||||
with just this patch, it is possible to see a difference for any
|
||||
source that does not report itself as fully allocated, coupled with a
|
||||
destination BDS that can quickly report that it already reads as zero.
|
||||
(For a source that reports as fully allocated, such as a file, the
|
||||
rest of mirror_dirty_init() still sets the entire dirty bitmap to
|
||||
true, so even though we avoided the pre-zeroing, we are not yet
|
||||
avoiding all redundant I/O).
|
||||
|
||||
Iotest 194 detects the difference made by this patch: for a file
|
||||
source (where block status reports the entire image as allocated, and
|
||||
therefore we end up writing zeroes everywhere in the destination
|
||||
anyways), the job length remains the same. But for a qcow2 source and
|
||||
a destination that reads as all zeroes, the dirty bitmap changes to
|
||||
just tracking the allocated portions of the source, which results in
|
||||
faster completion and smaller job statistics. For the test to pass
|
||||
with both ./check -file and -qcow2, a new python filter is needed to
|
||||
mask out the now-varying job amounts (this matches the shell filters
|
||||
_filter_block_job_{offset,len} in common.filter). A later test will
|
||||
also be added which further validates expected sparseness, so it does
|
||||
not matter that 194 is no longer explicitly looking at how many bytes
|
||||
were copied.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-25-eblake@redhat.com>
|
||||
Reviewed-by: Sunny Zhu <sunnyzhyy@qq.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
(cherry picked from commit 181a63667adf16c35b57e446def3e41c70f1fea6)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/mirror.c | 24 ++++++++++++++++--------
|
||||
tests/qemu-iotests/194 | 6 ++++--
|
||||
tests/qemu-iotests/194.out | 4 ++--
|
||||
tests/qemu-iotests/iotests.py | 12 +++++++++++-
|
||||
4 files changed, 33 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/block/mirror.c b/block/mirror.c
|
||||
index d04db85883..bca99ec206 100644
|
||||
--- a/block/mirror.c
|
||||
+++ b/block/mirror.c
|
||||
@@ -848,23 +848,31 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
|
||||
bdrv_can_write_zeroes_with_unmap(target_bs);
|
||||
|
||||
+ /* Determine if the image is already zero, regardless of sync mode. */
|
||||
bdrv_graph_co_rdlock();
|
||||
bs = s->mirror_top_bs->backing->bs;
|
||||
+ if (s->target_is_zero) {
|
||||
+ ret = 1;
|
||||
+ } else {
|
||||
+ ret = bdrv_co_is_all_zeroes(target_bs);
|
||||
+ }
|
||||
bdrv_graph_co_rdunlock();
|
||||
|
||||
- if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
+ /* Determine if a pre-zeroing pass is necessary. */
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
+ } else if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
/* In TOP mode, there is no benefit to a pre-zeroing pass. */
|
||||
- } else if (!s->target_is_zero || punch_holes) {
|
||||
+ } else if (ret == 0 || punch_holes) {
|
||||
/*
|
||||
* Here, we are in FULL mode; our goal is to avoid writing
|
||||
* zeroes if the destination already reads as zero, except
|
||||
* when we are trying to punch holes. This is possible if
|
||||
- * zeroing happened externally (s->target_is_zero) or if we
|
||||
- * have a fast way to pre-zero the image (the dirty bitmap
|
||||
- * will be populated later by the non-zero portions, the same
|
||||
- * as for TOP mode). If pre-zeroing is not fast, or we need
|
||||
- * to punch holes, then our only recourse is to write the
|
||||
- * entire image.
|
||||
+ * zeroing happened externally (ret > 0) or if we have a fast
|
||||
+ * way to pre-zero the image (the dirty bitmap will be
|
||||
+ * populated later by the non-zero portions, the same as for
|
||||
+ * TOP mode). If pre-zeroing is not fast, or we need to punch
|
||||
+ * holes, then our only recourse is to write the entire image.
|
||||
*/
|
||||
if (!bdrv_can_write_zeroes_with_unmap(target_bs)) {
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length);
|
||||
diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194
|
||||
index d0b9c084f5..e114c0b269 100755
|
||||
--- a/tests/qemu-iotests/194
|
||||
+++ b/tests/qemu-iotests/194
|
||||
@@ -62,7 +62,8 @@ with iotests.FilePath('source.img') as source_img_path, \
|
||||
|
||||
iotests.log('Waiting for `drive-mirror` to complete...')
|
||||
iotests.log(source_vm.event_wait('BLOCK_JOB_READY'),
|
||||
- filters=[iotests.filter_qmp_event])
|
||||
+ filters=[iotests.filter_qmp_event,
|
||||
+ iotests.filter_block_job])
|
||||
|
||||
iotests.log('Starting migration...')
|
||||
capabilities = [{'capability': 'events', 'state': True},
|
||||
@@ -88,7 +89,8 @@ with iotests.FilePath('source.img') as source_img_path, \
|
||||
|
||||
while True:
|
||||
event2 = source_vm.event_wait('BLOCK_JOB_COMPLETED')
|
||||
- iotests.log(event2, filters=[iotests.filter_qmp_event])
|
||||
+ iotests.log(event2, filters=[iotests.filter_qmp_event,
|
||||
+ iotests.filter_block_job])
|
||||
if event2['event'] == 'BLOCK_JOB_COMPLETED':
|
||||
iotests.log('Stopping the NBD server on destination...')
|
||||
iotests.log(dest_vm.qmp('nbd-server-stop'))
|
||||
diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out
|
||||
index 6940e809cd..d02655a514 100644
|
||||
--- a/tests/qemu-iotests/194.out
|
||||
+++ b/tests/qemu-iotests/194.out
|
||||
@@ -7,7 +7,7 @@ Launching NBD server on destination...
|
||||
Starting `drive-mirror` on source...
|
||||
{"return": {}}
|
||||
Waiting for `drive-mirror` to complete...
|
||||
-{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||
+{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||
Starting migration...
|
||||
{"return": {}}
|
||||
{"execute": "migrate-start-postcopy", "arguments": {}}
|
||||
@@ -18,7 +18,7 @@ Starting migration...
|
||||
{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||
Gracefully ending the `drive-mirror` job on source...
|
||||
{"return": {}}
|
||||
-{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||
+{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||
Stopping the NBD server on destination...
|
||||
{"return": {}}
|
||||
Wait for migration completion on target...
|
||||
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
|
||||
index 7292c8b342..05274772ce 100644
|
||||
--- a/tests/qemu-iotests/iotests.py
|
||||
+++ b/tests/qemu-iotests/iotests.py
|
||||
@@ -601,13 +601,23 @@ def filter_chown(msg):
|
||||
return chown_re.sub("chown UID:GID", msg)
|
||||
|
||||
def filter_qmp_event(event):
|
||||
- '''Filter a QMP event dict'''
|
||||
+ '''Filter the timestamp of a QMP event dict'''
|
||||
event = dict(event)
|
||||
if 'timestamp' in event:
|
||||
event['timestamp']['seconds'] = 'SECS'
|
||||
event['timestamp']['microseconds'] = 'USECS'
|
||||
return event
|
||||
|
||||
+def filter_block_job(event):
|
||||
+ '''Filter the offset and length of a QMP block job event dict'''
|
||||
+ event = dict(event)
|
||||
+ if 'data' in event:
|
||||
+ if 'offset' in event['data']:
|
||||
+ event['data']['offset'] = 'OFFSET'
|
||||
+ if 'len' in event['data']:
|
||||
+ event['data']['len'] = 'LEN'
|
||||
+ return event
|
||||
+
|
||||
def filter_qmp(qmsg, filter_fn):
|
||||
'''Given a string filter, filter a QMP object's values.
|
||||
filter_fn takes a (key, value) pair.'''
|
||||
--
|
||||
2.39.3
|
||||
|
355
kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch
Normal file
355
kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch
Normal file
@ -0,0 +1,355 @@
|
||||
From cc72e6ec30fb113b82fcdb61f79a0fae18e31e79 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:28 -0500
|
||||
Subject: [PATCH 11/14] mirror: Skip writing zeroes when target is already zero
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [11/14] 82df7fcf94606e6b6570469d3c05666da5039408 (ebblake/centos-qemu-kvm)
|
||||
|
||||
When mirroring, the goal is to ensure that the destination reads the
|
||||
same as the source; this goal is met whether the destination is sparse
|
||||
or fully-allocated (except when explicitly punching holes, then merely
|
||||
reading zero is not enough to know if it is sparse, so we still want
|
||||
to punch the hole). Avoiding a redundant write to zero (whether in
|
||||
the background because the zero cluster was marked in the dirty
|
||||
bitmap, or in the foreground because the guest is writing zeroes) when
|
||||
the destination already reads as zero makes mirroring faster, and
|
||||
avoids allocating the destination merely because the source reports as
|
||||
allocated.
|
||||
|
||||
The effect is especially pronounced when the source is a raw file.
|
||||
That's because when the source is a qcow2 file, the dirty bitmap only
|
||||
visits the portions of the source that are allocated, which tend to be
|
||||
non-zero. But when the source is a raw file,
|
||||
bdrv_co_is_allocated_above() reports the entire file as allocated so
|
||||
mirror_dirty_init sets the entire dirty bitmap, and it is only later
|
||||
during mirror_iteration that we change to consulting the more precise
|
||||
bdrv_co_block_status_above() to learn where the source reads as zero.
|
||||
|
||||
Remember that since a mirror operation can write a cluster more than
|
||||
once (every time the guest changes the source, the destination is also
|
||||
changed to keep up), and the guest can change whether a given cluster
|
||||
reads as zero, is discarded, or has non-zero data over the course of
|
||||
the mirror operation, we can't take the shortcut of relying on
|
||||
s->target_is_zero (which is static for the life of the job) in
|
||||
mirror_co_zero() to see if the destination is already zero, because
|
||||
that information may be stale. Any solution we use must be dynamic in
|
||||
the face of the guest writing or discarding a cluster while the mirror
|
||||
has been ongoing.
|
||||
|
||||
We could just teach mirror_co_zero() to do a block_status() probe of
|
||||
the destination, and skip the zeroes if the destination already reads
|
||||
as zero, but we know from past experience that extra block_status()
|
||||
calls are not always cheap (tmpfs, anyone?), especially when they are
|
||||
random access rather than linear. Use of block_status() of the source
|
||||
by the background task in a linear fashion is not our bottleneck (it's
|
||||
a background task, after all); but since mirroring can be done while
|
||||
the source is actively being changed, we don't want a slow
|
||||
block_status() of the destination to occur on the hot path of the
|
||||
guest trying to do random-access writes to the source.
|
||||
|
||||
So this patch takes a slightly different approach: any time we have to
|
||||
track dirty clusters, we can also track which clusters are known to
|
||||
read as zero. For sync=TOP or when we are punching holes from
|
||||
"detect-zeroes":"unmap", the zero bitmap starts out empty, but
|
||||
prevents a second write zero to a cluster that was already zero by an
|
||||
earlier pass; for sync=FULL when we are not punching holes, the zero
|
||||
bitmap starts out full if the destination reads as zero during
|
||||
initialization. Either way, I/O to the destination can now avoid
|
||||
redundant write zero to a cluster that already reads as zero, all
|
||||
without having to do a block_status() per write on the destination.
|
||||
|
||||
With this patch, if I create a raw sparse destination file, connect it
|
||||
with QMP 'blockdev-add' while leaving it at the default "discard":
|
||||
"ignore", then run QMP 'blockdev-mirror' with "sync": "full", the
|
||||
destination remains sparse rather than fully allocated. Meanwhile, a
|
||||
destination image that is already fully allocated remains so unless it
|
||||
was opened with "detect-zeroes": "unmap". And any time writing zeroes
|
||||
is skipped, the job counters are not incremented.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-26-eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
(cherry picked from commit 7e277545b90874171128804e256a538fb0e8dd7e)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
block/mirror.c | 107 ++++++++++++++++++++++++++++++++++++++++++-------
|
||||
1 file changed, 93 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/block/mirror.c b/block/mirror.c
|
||||
index bca99ec206..724318f037 100644
|
||||
--- a/block/mirror.c
|
||||
+++ b/block/mirror.c
|
||||
@@ -73,6 +73,7 @@ typedef struct MirrorBlockJob {
|
||||
size_t buf_size;
|
||||
int64_t bdev_length;
|
||||
unsigned long *cow_bitmap;
|
||||
+ unsigned long *zero_bitmap;
|
||||
BdrvDirtyBitmap *dirty_bitmap;
|
||||
BdrvDirtyBitmapIter *dbi;
|
||||
uint8_t *buf;
|
||||
@@ -108,9 +109,12 @@ struct MirrorOp {
|
||||
int64_t offset;
|
||||
uint64_t bytes;
|
||||
|
||||
- /* The pointee is set by mirror_co_read(), mirror_co_zero(), and
|
||||
- * mirror_co_discard() before yielding for the first time */
|
||||
+ /*
|
||||
+ * These pointers are set by mirror_co_read(), mirror_co_zero(), and
|
||||
+ * mirror_co_discard() before yielding for the first time
|
||||
+ */
|
||||
int64_t *bytes_handled;
|
||||
+ bool *io_skipped;
|
||||
|
||||
bool is_pseudo_op;
|
||||
bool is_active_write;
|
||||
@@ -408,15 +412,34 @@ static void coroutine_fn mirror_co_read(void *opaque)
|
||||
static void coroutine_fn mirror_co_zero(void *opaque)
|
||||
{
|
||||
MirrorOp *op = opaque;
|
||||
- int ret;
|
||||
+ bool write_needed = true;
|
||||
+ int ret = 0;
|
||||
|
||||
op->s->in_flight++;
|
||||
op->s->bytes_in_flight += op->bytes;
|
||||
*op->bytes_handled = op->bytes;
|
||||
op->is_in_flight = true;
|
||||
|
||||
- ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes,
|
||||
- op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0);
|
||||
+ if (op->s->zero_bitmap) {
|
||||
+ unsigned long end = DIV_ROUND_UP(op->offset + op->bytes,
|
||||
+ op->s->granularity);
|
||||
+ assert(QEMU_IS_ALIGNED(op->offset, op->s->granularity));
|
||||
+ assert(QEMU_IS_ALIGNED(op->bytes, op->s->granularity) ||
|
||||
+ op->offset + op->bytes == op->s->bdev_length);
|
||||
+ if (find_next_zero_bit(op->s->zero_bitmap, end,
|
||||
+ op->offset / op->s->granularity) == end) {
|
||||
+ write_needed = false;
|
||||
+ *op->io_skipped = true;
|
||||
+ }
|
||||
+ }
|
||||
+ if (write_needed) {
|
||||
+ ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes,
|
||||
+ op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0);
|
||||
+ }
|
||||
+ if (ret >= 0 && op->s->zero_bitmap) {
|
||||
+ bitmap_set(op->s->zero_bitmap, op->offset / op->s->granularity,
|
||||
+ DIV_ROUND_UP(op->bytes, op->s->granularity));
|
||||
+ }
|
||||
mirror_write_complete(op, ret);
|
||||
}
|
||||
|
||||
@@ -435,29 +458,43 @@ static void coroutine_fn mirror_co_discard(void *opaque)
|
||||
}
|
||||
|
||||
static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
|
||||
- unsigned bytes, MirrorMethod mirror_method)
|
||||
+ unsigned bytes, MirrorMethod mirror_method,
|
||||
+ bool *io_skipped)
|
||||
{
|
||||
MirrorOp *op;
|
||||
Coroutine *co;
|
||||
int64_t bytes_handled = -1;
|
||||
|
||||
+ assert(QEMU_IS_ALIGNED(offset, s->granularity));
|
||||
+ assert(QEMU_IS_ALIGNED(bytes, s->granularity) ||
|
||||
+ offset + bytes == s->bdev_length);
|
||||
op = g_new(MirrorOp, 1);
|
||||
*op = (MirrorOp){
|
||||
.s = s,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.bytes_handled = &bytes_handled,
|
||||
+ .io_skipped = io_skipped,
|
||||
};
|
||||
qemu_co_queue_init(&op->waiting_requests);
|
||||
|
||||
switch (mirror_method) {
|
||||
case MIRROR_METHOD_COPY:
|
||||
+ if (s->zero_bitmap) {
|
||||
+ bitmap_clear(s->zero_bitmap, offset / s->granularity,
|
||||
+ DIV_ROUND_UP(bytes, s->granularity));
|
||||
+ }
|
||||
co = qemu_coroutine_create(mirror_co_read, op);
|
||||
break;
|
||||
case MIRROR_METHOD_ZERO:
|
||||
+ /* s->zero_bitmap handled in mirror_co_zero */
|
||||
co = qemu_coroutine_create(mirror_co_zero, op);
|
||||
break;
|
||||
case MIRROR_METHOD_DISCARD:
|
||||
+ if (s->zero_bitmap) {
|
||||
+ bitmap_clear(s->zero_bitmap, offset / s->granularity,
|
||||
+ DIV_ROUND_UP(bytes, s->granularity));
|
||||
+ }
|
||||
co = qemu_coroutine_create(mirror_co_discard, op);
|
||||
break;
|
||||
default:
|
||||
@@ -568,6 +605,7 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
|
||||
int ret = -1;
|
||||
int64_t io_bytes;
|
||||
int64_t io_bytes_acct;
|
||||
+ bool io_skipped = false;
|
||||
MirrorMethod mirror_method = MIRROR_METHOD_COPY;
|
||||
|
||||
assert(!(offset % s->granularity));
|
||||
@@ -611,8 +649,10 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
|
||||
}
|
||||
|
||||
io_bytes = mirror_clip_bytes(s, offset, io_bytes);
|
||||
- io_bytes = mirror_perform(s, offset, io_bytes, mirror_method);
|
||||
- if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) {
|
||||
+ io_bytes = mirror_perform(s, offset, io_bytes, mirror_method,
|
||||
+ &io_skipped);
|
||||
+ if (io_skipped ||
|
||||
+ (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok)) {
|
||||
io_bytes_acct = 0;
|
||||
} else {
|
||||
io_bytes_acct = io_bytes;
|
||||
@@ -847,8 +887,10 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
bool punch_holes =
|
||||
target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
|
||||
bdrv_can_write_zeroes_with_unmap(target_bs);
|
||||
+ int64_t bitmap_length = DIV_ROUND_UP(s->bdev_length, s->granularity);
|
||||
|
||||
/* Determine if the image is already zero, regardless of sync mode. */
|
||||
+ s->zero_bitmap = bitmap_new(bitmap_length);
|
||||
bdrv_graph_co_rdlock();
|
||||
bs = s->mirror_top_bs->backing->bs;
|
||||
if (s->target_is_zero) {
|
||||
@@ -862,7 +904,14 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
} else if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
- /* In TOP mode, there is no benefit to a pre-zeroing pass. */
|
||||
+ /*
|
||||
+ * In TOP mode, there is no benefit to a pre-zeroing pass, but
|
||||
+ * the zero bitmap can be set if the destination already reads
|
||||
+ * as zero and we are not punching holes.
|
||||
+ */
|
||||
+ if (ret > 0 && !punch_holes) {
|
||||
+ bitmap_set(s->zero_bitmap, 0, bitmap_length);
|
||||
+ }
|
||||
} else if (ret == 0 || punch_holes) {
|
||||
/*
|
||||
* Here, we are in FULL mode; our goal is to avoid writing
|
||||
@@ -871,8 +920,9 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
* zeroing happened externally (ret > 0) or if we have a fast
|
||||
* way to pre-zero the image (the dirty bitmap will be
|
||||
* populated later by the non-zero portions, the same as for
|
||||
- * TOP mode). If pre-zeroing is not fast, or we need to punch
|
||||
- * holes, then our only recourse is to write the entire image.
|
||||
+ * TOP mode). If pre-zeroing is not fast, then our only
|
||||
+ * recourse is to mark the entire image dirty. The act of
|
||||
+ * pre-zeroing will populate the zero bitmap.
|
||||
*/
|
||||
if (!bdrv_can_write_zeroes_with_unmap(target_bs)) {
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length);
|
||||
@@ -883,6 +933,7 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
for (offset = 0; offset < s->bdev_length; ) {
|
||||
int bytes = MIN(s->bdev_length - offset,
|
||||
QEMU_ALIGN_DOWN(INT_MAX, s->granularity));
|
||||
+ bool ignored;
|
||||
|
||||
mirror_throttle(s);
|
||||
|
||||
@@ -898,12 +949,15 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
|
||||
continue;
|
||||
}
|
||||
|
||||
- mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO);
|
||||
+ mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO, &ignored);
|
||||
offset += bytes;
|
||||
}
|
||||
|
||||
mirror_wait_for_all_io(s);
|
||||
s->initial_zeroing_ongoing = false;
|
||||
+ } else {
|
||||
+ /* In FULL mode, and image already reads as zero. */
|
||||
+ bitmap_set(s->zero_bitmap, 0, bitmap_length);
|
||||
}
|
||||
|
||||
/* First part, loop on the sectors and initialize the dirty bitmap. */
|
||||
@@ -1188,6 +1242,7 @@ immediate_exit:
|
||||
assert(s->in_flight == 0);
|
||||
qemu_vfree(s->buf);
|
||||
g_free(s->cow_bitmap);
|
||||
+ g_free(s->zero_bitmap);
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_dirty_iter_free(s->dbi);
|
||||
|
||||
@@ -1367,6 +1422,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||
int ret;
|
||||
size_t qiov_offset = 0;
|
||||
int64_t dirty_bitmap_offset, dirty_bitmap_end;
|
||||
+ int64_t zero_bitmap_offset, zero_bitmap_end;
|
||||
|
||||
if (!QEMU_IS_ALIGNED(offset, job->granularity) &&
|
||||
bdrv_dirty_bitmap_get(job->dirty_bitmap, offset))
|
||||
@@ -1410,8 +1466,9 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||
}
|
||||
|
||||
/*
|
||||
- * Tails are either clean or shrunk, so for bitmap resetting
|
||||
- * we safely align the range down.
|
||||
+ * Tails are either clean or shrunk, so for dirty bitmap resetting
|
||||
+ * we safely align the range narrower. But for zero bitmap, round
|
||||
+ * range wider for checking or clearing, and narrower for setting.
|
||||
*/
|
||||
dirty_bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity);
|
||||
dirty_bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity);
|
||||
@@ -1419,22 +1476,44 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||
bdrv_reset_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset,
|
||||
dirty_bitmap_end - dirty_bitmap_offset);
|
||||
}
|
||||
+ zero_bitmap_offset = offset / job->granularity;
|
||||
+ zero_bitmap_end = DIV_ROUND_UP(offset + bytes, job->granularity);
|
||||
|
||||
job_progress_increase_remaining(&job->common.job, bytes);
|
||||
job->active_write_bytes_in_flight += bytes;
|
||||
|
||||
switch (method) {
|
||||
case MIRROR_METHOD_COPY:
|
||||
+ if (job->zero_bitmap) {
|
||||
+ bitmap_clear(job->zero_bitmap, zero_bitmap_offset,
|
||||
+ zero_bitmap_end - zero_bitmap_offset);
|
||||
+ }
|
||||
ret = blk_co_pwritev_part(job->target, offset, bytes,
|
||||
qiov, qiov_offset, flags);
|
||||
break;
|
||||
|
||||
case MIRROR_METHOD_ZERO:
|
||||
+ if (job->zero_bitmap) {
|
||||
+ if (find_next_zero_bit(job->zero_bitmap, zero_bitmap_end,
|
||||
+ zero_bitmap_offset) == zero_bitmap_end) {
|
||||
+ ret = 0;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
assert(!qiov);
|
||||
ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags);
|
||||
+ if (job->zero_bitmap && ret >= 0) {
|
||||
+ bitmap_set(job->zero_bitmap, dirty_bitmap_offset / job->granularity,
|
||||
+ (dirty_bitmap_end - dirty_bitmap_offset) /
|
||||
+ job->granularity);
|
||||
+ }
|
||||
break;
|
||||
|
||||
case MIRROR_METHOD_DISCARD:
|
||||
+ if (job->zero_bitmap) {
|
||||
+ bitmap_clear(job->zero_bitmap, zero_bitmap_offset,
|
||||
+ zero_bitmap_end - zero_bitmap_offset);
|
||||
+ }
|
||||
assert(!qiov);
|
||||
ret = blk_co_pdiscard(job->target, offset, bytes);
|
||||
break;
|
||||
--
|
||||
2.39.3
|
||||
|
545
kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch
Normal file
545
kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch
Normal file
@ -0,0 +1,545 @@
|
||||
From 2bb881df5b93f5534e5f0b91cf1ed3e0b524c2d3 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:30 -0500
|
||||
Subject: [PATCH 13/14] tests: Add iotest mirror-sparse for recent patches
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 363: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-88435 RHEL-88437
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [13/14] 474f12dfe9161c7e9f59cafde203e5183e2fc3f5 (ebblake/centos-qemu-kvm)
|
||||
|
||||
Prove that blockdev-mirror can now result in sparse raw destination
|
||||
files, regardless of whether the source is raw or qcow2. By making
|
||||
this a separate test, it was possible to test effects of individual
|
||||
patches for the various pieces that all have to work together for a
|
||||
sparse mirror to be successful.
|
||||
|
||||
Note that ./check -file produces different job lengths than ./check
|
||||
-qcow2 (the test uses a filter to normalize); that's because when
|
||||
deciding how much of the image to be mirrored, the code looks at how
|
||||
much of the source image was allocated (for qcow2, this is only the
|
||||
written clusters; for raw, it is the entire file). But the important
|
||||
part is that the destination file ends up smaller than 3M, rather than
|
||||
the 20M it used to be before this patch series.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20250509204341.3553601-28-eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
(cherry picked from commit c0ddcb2cbc146e64f666eaae4edc7b5db7e5814d)
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88435
|
||||
Jira: https://issues.redhat.com/browse/RHEL-88437
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
tests/qemu-iotests/tests/mirror-sparse | 125 +++++++
|
||||
tests/qemu-iotests/tests/mirror-sparse.out | 365 +++++++++++++++++++++
|
||||
2 files changed, 490 insertions(+)
|
||||
create mode 100755 tests/qemu-iotests/tests/mirror-sparse
|
||||
create mode 100644 tests/qemu-iotests/tests/mirror-sparse.out
|
||||
|
||||
diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse
|
||||
new file mode 100755
|
||||
index 0000000000..8c52a4e244
|
||||
--- /dev/null
|
||||
+++ b/tests/qemu-iotests/tests/mirror-sparse
|
||||
@@ -0,0 +1,125 @@
|
||||
+#!/usr/bin/env bash
|
||||
+# group: rw auto quick
|
||||
+#
|
||||
+# Test blockdev-mirror with raw sparse destination
|
||||
+#
|
||||
+# Copyright (C) 2025 Red Hat, Inc.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify
|
||||
+# it under the terms of the GNU General Public License as published by
|
||||
+# the Free Software Foundation; either version 2 of the License, or
|
||||
+# (at your option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful,
|
||||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+# GNU General Public License for more details.
|
||||
+#
|
||||
+# You should have received a copy of the GNU General Public License
|
||||
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
+#
|
||||
+
|
||||
+seq="$(basename $0)"
|
||||
+echo "QA output created by $seq"
|
||||
+
|
||||
+status=1 # failure is the default!
|
||||
+
|
||||
+_cleanup()
|
||||
+{
|
||||
+ _cleanup_test_img
|
||||
+ _cleanup_qemu
|
||||
+}
|
||||
+trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
+
|
||||
+# get standard environment, filters and checks
|
||||
+cd ..
|
||||
+. ./common.rc
|
||||
+. ./common.filter
|
||||
+. ./common.qemu
|
||||
+
|
||||
+_supported_fmt qcow2 raw # Format of the source. dst is always raw file
|
||||
+_supported_proto file
|
||||
+_supported_os Linux
|
||||
+
|
||||
+echo
|
||||
+echo "=== Initial image setup ==="
|
||||
+echo
|
||||
+
|
||||
+TEST_IMG="$TEST_IMG.base" _make_test_img 20M
|
||||
+$QEMU_IO -c 'w 8M 2M' -f $IMGFMT "$TEST_IMG.base" | _filter_qemu_io
|
||||
+
|
||||
+_launch_qemu \
|
||||
+ -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false},
|
||||
+ "filename":"'"$TEST_IMG.base"'", "node-name":"src-file"}' \
|
||||
+ -blockdev '{"driver":"'$IMGFMT'", "node-name":"src", "file":"src-file"}'
|
||||
+h1=$QEMU_HANDLE
|
||||
+_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return'
|
||||
+
|
||||
+# Check several combinations; most should result in a sparse destination;
|
||||
+# the destination should only be fully allocated if pre-allocated
|
||||
+# and not punching holes due to detect-zeroes
|
||||
+# do_test creation discard zeroes result
|
||||
+do_test() {
|
||||
+ creation=$1
|
||||
+ discard=$2
|
||||
+ zeroes=$3
|
||||
+ expected=$4
|
||||
+
|
||||
+echo
|
||||
+echo "=== Testing creation=$creation discard=$discard zeroes=$zeroes ==="
|
||||
+echo
|
||||
+
|
||||
+rm -f $TEST_IMG
|
||||
+if test $creation = external; then
|
||||
+ truncate --size=20M $TEST_IMG
|
||||
+else
|
||||
+ _send_qemu_cmd $h1 '{"execute": "blockdev-create", "arguments":
|
||||
+ {"options": {"driver":"file", "filename":"'$TEST_IMG'",
|
||||
+ "size":'$((20*1024*1024))', "preallocation":"'$creation'"},
|
||||
+ "job-id":"job1"}}' 'concluded'
|
||||
+ _send_qemu_cmd $h1 '{"execute": "job-dismiss", "arguments":
|
||||
+ {"id": "job1"}}' 'return'
|
||||
+fi
|
||||
+_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"'$TEST_IMG'", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"'$discard'",
|
||||
+ "detect-zeroes":"'$zeroes'"}}' 'return'
|
||||
+_send_qemu_cmd $h1 '{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}' 'return'
|
||||
+_timed_wait_for $h1 '"ready"'
|
||||
+_send_qemu_cmd $h1 '{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}' 'return' \
|
||||
+ | _filter_block_job_offset | _filter_block_job_len
|
||||
+_send_qemu_cmd $h1 '{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}' 'return' \
|
||||
+ | _filter_block_job_offset | _filter_block_job_len
|
||||
+$QEMU_IMG compare -U -f $IMGFMT -F raw $TEST_IMG.base $TEST_IMG
|
||||
+result=$(disk_usage $TEST_IMG)
|
||||
+if test $result -lt $((3*1024*1024)); then
|
||||
+ actual=sparse
|
||||
+elif test $result = $((20*1024*1024)); then
|
||||
+ actual=full
|
||||
+else
|
||||
+ actual=unknown
|
||||
+fi
|
||||
+echo "Destination is $actual; expected $expected"
|
||||
+}
|
||||
+
|
||||
+do_test external ignore off sparse
|
||||
+do_test external unmap off sparse
|
||||
+do_test external unmap unmap sparse
|
||||
+do_test off ignore off sparse
|
||||
+do_test off unmap off sparse
|
||||
+do_test off unmap unmap sparse
|
||||
+do_test full ignore off full
|
||||
+do_test full unmap off sparse
|
||||
+do_test full unmap unmap sparse
|
||||
+
|
||||
+_send_qemu_cmd $h1 '{"execute":"quit"}' ''
|
||||
+
|
||||
+# success, all done
|
||||
+echo '*** done'
|
||||
+rm -f $seq.full
|
||||
+status=0
|
||||
diff --git a/tests/qemu-iotests/tests/mirror-sparse.out b/tests/qemu-iotests/tests/mirror-sparse.out
|
||||
new file mode 100644
|
||||
index 0000000000..2103b891c3
|
||||
--- /dev/null
|
||||
+++ b/tests/qemu-iotests/tests/mirror-sparse.out
|
||||
@@ -0,0 +1,365 @@
|
||||
+QA output created by mirror-sparse
|
||||
+
|
||||
+=== Initial image setup ===
|
||||
+
|
||||
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=20971520
|
||||
+wrote 2097152/2097152 bytes at offset 8388608
|
||||
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
+{"execute": "qmp_capabilities"}
|
||||
+{"return": {}}
|
||||
+
|
||||
+=== Testing creation=external discard=ignore zeroes=off ===
|
||||
+
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"ignore",
|
||||
+ "detect-zeroes":"off"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+
|
||||
+=== Testing creation=external discard=unmap zeroes=off ===
|
||||
+
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"unmap",
|
||||
+ "detect-zeroes":"off"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+
|
||||
+=== Testing creation=external discard=unmap zeroes=unmap ===
|
||||
+
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"unmap",
|
||||
+ "detect-zeroes":"unmap"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+
|
||||
+=== Testing creation=off discard=ignore zeroes=off ===
|
||||
+
|
||||
+{"execute": "blockdev-create", "arguments":
|
||||
+ {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT",
|
||||
+ "size":20971520, "preallocation":"off"},
|
||||
+ "job-id":"job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}}
|
||||
+{"execute": "job-dismiss", "arguments":
|
||||
+ {"id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"ignore",
|
||||
+ "detect-zeroes":"off"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+
|
||||
+=== Testing creation=off discard=unmap zeroes=off ===
|
||||
+
|
||||
+{"execute": "blockdev-create", "arguments":
|
||||
+ {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT",
|
||||
+ "size":20971520, "preallocation":"off"},
|
||||
+ "job-id":"job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}}
|
||||
+{"execute": "job-dismiss", "arguments":
|
||||
+ {"id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"unmap",
|
||||
+ "detect-zeroes":"off"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+
|
||||
+=== Testing creation=off discard=unmap zeroes=unmap ===
|
||||
+
|
||||
+{"execute": "blockdev-create", "arguments":
|
||||
+ {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT",
|
||||
+ "size":20971520, "preallocation":"off"},
|
||||
+ "job-id":"job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}}
|
||||
+{"execute": "job-dismiss", "arguments":
|
||||
+ {"id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"unmap",
|
||||
+ "detect-zeroes":"unmap"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+
|
||||
+=== Testing creation=full discard=ignore zeroes=off ===
|
||||
+
|
||||
+{"execute": "blockdev-create", "arguments":
|
||||
+ {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT",
|
||||
+ "size":20971520, "preallocation":"full"},
|
||||
+ "job-id":"job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}}
|
||||
+{"execute": "job-dismiss", "arguments":
|
||||
+ {"id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"ignore",
|
||||
+ "detect-zeroes":"off"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is full; expected full
|
||||
+
|
||||
+=== Testing creation=full discard=unmap zeroes=off ===
|
||||
+
|
||||
+{"execute": "blockdev-create", "arguments":
|
||||
+ {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT",
|
||||
+ "size":20971520, "preallocation":"full"},
|
||||
+ "job-id":"job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}}
|
||||
+{"execute": "job-dismiss", "arguments":
|
||||
+ {"id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"unmap",
|
||||
+ "detect-zeroes":"off"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+
|
||||
+=== Testing creation=full discard=unmap zeroes=unmap ===
|
||||
+
|
||||
+{"execute": "blockdev-create", "arguments":
|
||||
+ {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT",
|
||||
+ "size":20971520, "preallocation":"full"},
|
||||
+ "job-id":"job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}}
|
||||
+{"execute": "job-dismiss", "arguments":
|
||||
+ {"id": "job1"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-add", "arguments":
|
||||
+ {"node-name": "dst", "driver":"file",
|
||||
+ "filename":"TEST_DIR/t.IMGFMT", "aio":"threads",
|
||||
+ "auto-read-only":true, "discard":"unmap",
|
||||
+ "detect-zeroes":"unmap"}}
|
||||
+{"return": {}}
|
||||
+{"execute":"blockdev-mirror", "arguments":
|
||||
+ {"sync":"full", "device":"src", "target":"dst",
|
||||
+ "job-id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}}
|
||||
+{"execute": "job-complete", "arguments":
|
||||
+ {"id":"job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"return": {}}
|
||||
+{"execute": "blockdev-del", "arguments":
|
||||
+ {"node-name": "dst"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}}
|
||||
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}}
|
||||
+{"return": {}}
|
||||
+Images are identical.
|
||||
+Destination is sparse; expected sparse
|
||||
+{"execute":"quit"}
|
||||
+*** done
|
||||
--
|
||||
2.39.3
|
||||
|
@ -147,7 +147,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \
|
||||
Summary: QEMU is a machine emulator and virtualizer
|
||||
Name: qemu-kvm
|
||||
Version: 10.0.0
|
||||
Release: 3%{?rcrel}%{?dist}%{?cc_suffix}
|
||||
Release: 4%{?rcrel}%{?dist}%{?cc_suffix}
|
||||
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
|
||||
# Epoch 15 used for RHEL 8
|
||||
# Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5)
|
||||
@ -202,6 +202,48 @@ Patch24: kvm-migration-postcopy-Spatial-locality-page-hint-for-pr.patch
|
||||
Patch25: kvm-meson-configure-add-valgrind-option-en-dis-able-valg.patch
|
||||
# Fixing s390x build issues
|
||||
Patch26: kvm-docs-Don-t-define-duplicate-label-in-qemu-block-driv.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch27: kvm-block-Expand-block-status-mode-from-bool-to-flags.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch28: kvm-file-posix-gluster-Handle-zero-block-status-hint-bet.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch29: kvm-block-Let-bdrv_co_is_zero_fast-consolidate-adjacent-.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch30: kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch31: kvm-iotests-Improve-iotest-194-to-mirror-data.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch32: kvm-mirror-Minor-refactoring.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch33: kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch34: kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch35: kvm-mirror-Drop-redundant-zero_target-parameter.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch36: kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch37: kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch38: kvm-iotests-common.rc-add-disk_usage-function.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch39: kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch
|
||||
# For RHEL-88435 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1]
|
||||
# For RHEL-88437 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1]
|
||||
Patch40: kvm-mirror-Reduce-I-O-when-destination-is-detect-zeroes-.patch
|
||||
|
||||
%if %{have_clang}
|
||||
BuildRequires: clang
|
||||
@ -1284,6 +1326,26 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon May 26 2025 Miroslav Rezanina <mrezanin@redhat.com> - 10.0.0-4
|
||||
- kvm-block-Expand-block-status-mode-from-bool-to-flags.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-file-posix-gluster-Handle-zero-block-status-hint-bet.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-block-Let-bdrv_co_is_zero_fast-consolidate-adjacent-.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-iotests-Improve-iotest-194-to-mirror-data.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-mirror-Minor-refactoring.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-mirror-Drop-redundant-zero_target-parameter.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-iotests-common.rc-add-disk_usage-function.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch [RHEL-88435 RHEL-88437]
|
||||
- kvm-mirror-Reduce-I-O-when-destination-is-detect-zeroes-.patch [RHEL-88435 RHEL-88437]
|
||||
- Resolves: RHEL-88435
|
||||
(--migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-10.1])
|
||||
- Resolves: RHEL-88437
|
||||
(Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-10.1])
|
||||
|
||||
* Mon May 19 2025 Miroslav Rezanina <mrezanin@redhat.com> - 10.0.0-3
|
||||
- kvm-migration-postcopy-Spatial-locality-page-hint-for-pr.patch [RHEL-85635]
|
||||
- kvm-meson-configure-add-valgrind-option-en-dis-able-valg.patch [RHEL-88457]
|
||||
|
Loading…
Reference in New Issue
Block a user