* Mon Jun 16 2025 Jon Maloy <jmaloy@redhat.com> - 9.1.0-24
- kvm-s390x-pci-add-support-for-guests-that-request-direct.patch [RHEL-11430] - kvm-s390x-pci-indicate-QEMU-supports-relaxed-translation.patch [RHEL-11430] - kvm-block-Expand-block-status-mode-from-bool-to-flags.patch [RHEL-82906 RHEL-83015] - kvm-file-posix-gluster-Handle-zero-block-status-hint-bet.patch [RHEL-82906 RHEL-83015] - kvm-block-Let-bdrv_co_is_zero_fast-consolidate-adjacent-.patch [RHEL-82906 RHEL-83015] - kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch [RHEL-82906 RHEL-83015] - kvm-iotests-Improve-iotest-194-to-mirror-data.patch [RHEL-82906 RHEL-83015] - kvm-mirror-Minor-refactoring.patch [RHEL-82906 RHEL-83015] - kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch [RHEL-82906 RHEL-83015] - kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch [RHEL-82906 RHEL-83015] - kvm-mirror-Drop-redundant-zero_target-parameter.patch [RHEL-82906 RHEL-83015] - kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch [RHEL-82906 RHEL-83015] - kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch [RHEL-82906 RHEL-83015] - kvm-iotests-common.rc-add-disk_usage-function.patch [RHEL-82906 RHEL-83015] - kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch [RHEL-82906 RHEL-83015] - kvm-mirror-Reduce-I-O-when-destination-is-detect-zeroes-.patch [RHEL-82906 RHEL-83015] - Resolves: RHEL-11430 ([IBM 9.7 FEAT] KVM: Performance Enhanced Refresh PCI Translation - qemu part) - Resolves: RHEL-82906 (--migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]) - Resolves: RHEL-83015 (Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7])
This commit is contained in:
parent
fe09579d2f
commit
0dc0c63dbf
145
kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch
Normal file
145
kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch
Normal file
@ -0,0 +1,145 @@
|
||||
From f2cd96a040dd7863484d22a3995a2904605dadde Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:21 -0500
|
||||
Subject: [PATCH 06/16] block: Add new bdrv_co_is_all_zeroes() function
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [4/14] aabcba8323df698a72842f299e9242a5eee3aea6 (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)
|
||||
Conflicts:
|
||||
block/io.c - context with header names
|
||||
Jira: https://issues.redhat.com/browse/RHEL-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 293c5dd393..1f01337599 100644
|
||||
--- a/block/io.c
|
||||
+++ b/block/io.c
|
||||
@@ -38,10 +38,14 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/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);
|
||||
|
||||
@@ -2774,6 +2778,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.48.1
|
||||
|
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 26f5d221dd16137bed3527ee120cdf085e2c7e23 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:18 -0500
|
||||
Subject: [PATCH 03/16] block: Expand block status mode from bool to flags
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [1/14] 9de5245def80e9815ed306e4abce9caec56cef6f (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 c95c818c38..736ae2b56b 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 853e01a1eb..36488cdeca 100644
|
||||
--- a/block/copy-before-write.c
|
||||
+++ b/block/copy-before-write.c
|
||||
@@ -290,8 +290,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 f3226682d6..811ef12e43 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 f17a3f4d10..9ca55620ca 100644
|
||||
--- a/block/file-posix.c
|
||||
+++ b/block/file-posix.c
|
||||
@@ -3277,7 +3277,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,
|
||||
@@ -3293,7 +3293,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 f8b415f381..ae5c45666b 100644
|
||||
--- a/block/gluster.c
|
||||
+++ b/block/gluster.c
|
||||
@@ -1466,7 +1466,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,
|
||||
@@ -1483,7 +1483,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 3e189837a1..daaafe00d7 100644
|
||||
--- a/block/io.c
|
||||
+++ b/block/io.c
|
||||
@@ -2360,10 +2360,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.
|
||||
@@ -2383,7 +2381,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)
|
||||
{
|
||||
@@ -2472,7 +2470,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);
|
||||
|
||||
@@ -2484,10 +2482,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))
|
||||
{
|
||||
@@ -2544,7 +2542,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;
|
||||
}
|
||||
@@ -2556,7 +2554,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) {
|
||||
@@ -2565,14 +2563,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
|
||||
@@ -2623,7 +2621,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,
|
||||
@@ -2650,7 +2648,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) {
|
||||
@@ -2667,7 +2665,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) {
|
||||
@@ -2730,7 +2728,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);
|
||||
}
|
||||
|
||||
@@ -2761,8 +2760,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;
|
||||
@@ -2778,9 +2778,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;
|
||||
}
|
||||
@@ -2813,7 +2813,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) {
|
||||
@@ -3710,8 +3711,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)
|
||||
{
|
||||
@@ -3729,7 +3730,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 979bf90cb7..d7caa4b363 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 d464315766..a359aa236e 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 4730acc1eb..95021230c8 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 9205a0864f..22ea7834fd 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 c2f89db055..2e18c42d8f 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 a4cffb628c..788da07fee 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 fa5bc11085..b135e981e5 100644
|
||||
--- a/block/qed.c
|
||||
+++ b/block/qed.c
|
||||
@@ -832,9 +832,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 db8fe891c4..bb4ed9483e 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 ac7e8495f6..623bca87a6 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 9c0fd0cb3f..627f8eb05a 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 84d0d13f86..972b8f2e68 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 6363da08ce..028fe68488 100644
|
||||
--- a/block/vdi.c
|
||||
+++ b/block/vdi.c
|
||||
@@ -521,8 +521,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 78f6433607..6f1af82078 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 d95a204612..0dd641b614 100644
|
||||
--- a/block/vpc.c
|
||||
+++ b/block/vpc.c
|
||||
@@ -721,7 +721,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 8ffe8b3b9b..d59231357e 100644
|
||||
--- a/block/vvfat.c
|
||||
+++ b/block/vvfat.c
|
||||
@@ -3135,9 +3135,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 7030669f04..5beee6402b 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 3766d5de6b..373b72fdd8 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.48.1
|
||||
|
@ -0,0 +1,90 @@
|
||||
From 9f8158e56beae4221e91feb5a98cb4db9076cac4 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:20 -0500
|
||||
Subject: [PATCH 05/16] block: Let bdrv_co_is_zero_fast consolidate adjacent
|
||||
extents
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [3/14] 98bf9ff773d9a36f8a8e294e38629e3f20c41334 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 daaafe00d7..293c5dd393 100644
|
||||
--- a/block/io.c
|
||||
+++ b/block/io.c
|
||||
@@ -2747,28 +2747,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.48.1
|
||||
|
@ -0,0 +1,64 @@
|
||||
From 39e0c370357a414abacd64fb6a172e7b25eb4d82 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:19 -0500
|
||||
Subject: [PATCH 04/16] file-posix, gluster: Handle zero block status hint
|
||||
better
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [2/14] 1f7b47ce5f5fb321aee41a16accf5bce3d1bfe95 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 9ca55620ca..ce5da2b4c2 100644
|
||||
--- a/block/file-posix.c
|
||||
+++ b/block/file-posix.c
|
||||
@@ -3293,7 +3293,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 ae5c45666b..175c70164c 100644
|
||||
--- a/block/gluster.c
|
||||
+++ b/block/gluster.c
|
||||
@@ -1483,7 +1483,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.48.1
|
||||
|
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 8832268a98104ba3065a57dedcd3db43231512ba Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:22 -0500
|
||||
Subject: [PATCH 07/16] iotests: Improve iotest 194 to mirror data
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [5/14] bfbe8eab1035480cef9d69d1974ba66b755b1b60 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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.48.1
|
||||
|
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 644f39de9e2466a9570833b1070acf47a53863ea 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 14/16] iotests/common.rc: add disk_usage function
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [12/14] 0e5d4217f97fe6e952de23eedbc2b8d9c7600665 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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.48.1
|
||||
|
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 a5f6042a0c80daf3672fa071b724cb05e6f6e928 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:25 -0500
|
||||
Subject: [PATCH 10/16] mirror: Allow QMP override to declare target already
|
||||
zero
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [8/14] fb054864175d83e9d232464295b170808bee0e6c (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 c8bbaa0b35..bba3e3b05c 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 70046b6690..db11a99312 100644
|
||||
--- a/blockdev.c
|
||||
+++ b/blockdev.c
|
||||
@@ -2795,7 +2795,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,
|
||||
@@ -2906,11 +2906,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)
|
||||
@@ -2925,6 +2924,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);
|
||||
@@ -3041,6 +3041,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();
|
||||
|
||||
|
||||
@@ -3052,7 +3054,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,
|
||||
@@ -3082,6 +3084,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;
|
||||
@@ -3112,7 +3115,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 c1af3d1f7d..3969c60b93 100644
|
||||
--- a/qapi/block-core.json
|
||||
+++ b/qapi/block-core.json
|
||||
@@ -2535,6 +2535,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::
|
||||
@@ -2554,7 +2559,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 373b72fdd8..033711d8d7 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.48.1
|
||||
|
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 5040f835f07f3355ae80b3da2ae83ce35de022e0 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:26 -0500
|
||||
Subject: [PATCH 11/16] mirror: Drop redundant zero_target parameter
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [9/14] b84a938c69e3761211b9fee4c59b465d55f61855 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 bba3e3b05c..b35d12adaa 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 db11a99312..04fa759e30 100644
|
||||
--- a/blockdev.c
|
||||
+++ b/blockdev.c
|
||||
@@ -2795,7 +2795,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,
|
||||
@@ -2862,10 +2862,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);
|
||||
@@ -2907,7 +2903,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);
|
||||
}
|
||||
@@ -2923,7 +2919,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;
|
||||
|
||||
@@ -3038,9 +3033,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();
|
||||
@@ -3054,7 +3046,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,
|
||||
@@ -3091,7 +3083,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);
|
||||
@@ -3104,8 +3095,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);
|
||||
@@ -3115,7 +3104,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 033711d8d7..373b72fdd8 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.48.1
|
||||
|
92
kvm-mirror-Minor-refactoring.patch
Normal file
92
kvm-mirror-Minor-refactoring.patch
Normal file
@ -0,0 +1,92 @@
|
||||
From 0102da22fe5aefde9d398d539fc290ab062346f1 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:23 -0500
|
||||
Subject: [PATCH 08/16] mirror: Minor refactoring
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [6/14] 886fa2e3249f48f89d3e04ba619d370031851d89 (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)
|
||||
Conflicts:
|
||||
block/mirror.c - commit 5791ba52 not present
|
||||
Jira: https://issues.redhat.com/browse/RHEL-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 61f0a717b7..22f8bd98c4 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;
|
||||
+ 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.48.1
|
||||
|
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 482db3e637a16d5877e523e87c53ddb2579b4b66 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:24 -0500
|
||||
Subject: [PATCH 09/16] mirror: Pass full sync mode rather than bool to
|
||||
internals
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [7/14] f45a83a14b0eea07517176d44ab0c49db8233ea0 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 22f8bd98c4..c8bbaa0b35 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.48.1
|
||||
|
@ -0,0 +1,58 @@
|
||||
From be6ce2c91fe949d1c264de974ab4f6c4efc6976e Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Tue, 13 May 2025 17:00:45 -0500
|
||||
Subject: [PATCH 16/16] mirror: Reduce I/O when destination is
|
||||
detect-zeroes:unmap
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [14/14] 66f3de2ba9f977c9bc1c54f67d76b366df132e62 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 7f3b5477ce..87c19ddf0d 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.48.1
|
||||
|
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 423ce7727eecae647330287e1264ac0d938fa7f9 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:27 -0500
|
||||
Subject: [PATCH 12/16] mirror: Skip pre-zeroing destination if it is already
|
||||
zero
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [10/14] e754ae559123099f4aed322f6a4287cf3323f54d (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 b35d12adaa..29cac1777c 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 376ed1d2e6..84e0fc34be 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": {}}
|
||||
@@ -17,7 +17,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 c8cb028c2d..978bef1499 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.48.1
|
||||
|
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 8a2e660ff3ec7f7506fbd4197d4dc8f53db7859a Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:28 -0500
|
||||
Subject: [PATCH 13/16] mirror: Skip writing zeroes when target is already zero
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [11/14] f6bb5e0cecee07af0389aa18c3bddb47d6c5cf54 (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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 29cac1777c..7f3b5477ce 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;
|
||||
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.48.1
|
||||
|
256
kvm-s390x-pci-add-support-for-guests-that-request-direct.patch
Normal file
256
kvm-s390x-pci-add-support-for-guests-that-request-direct.patch
Normal file
@ -0,0 +1,256 @@
|
||||
From c60d0770ff3f9124e6e9d7beb03e1ef8067e8e26 Mon Sep 17 00:00:00 2001
|
||||
From: Christoph Schlameuss <cschlame@redhat.com>
|
||||
Date: Thu, 12 Jun 2025 13:25:32 +0200
|
||||
Subject: [PATCH 01/16] s390x/pci: add support for guests that request direct
|
||||
mapping
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Christoph Schlameuss <None>
|
||||
RH-MergeRequest: 376: Draft: KVM: Performance Enhanced Refresh PCI Translation
|
||||
RH-Jira: RHEL-11430
|
||||
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
||||
RH-Acked-by: Cédric Le Goater <clg@redhat.com>
|
||||
RH-Commit: [1/2] 11d1dd9a5add55ae43d5d922588a33945ecbfe27 (cschlame/qemu-kvm)
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-11430
|
||||
Conflicts: hw/s390x/s390-pci-bus.c old s390_pci_device_properties[] still has DEFINE_PROP_END_OF_LIST()
|
||||
hw/s390x/s390-pci-inst.c hw_accel.h is still in sysemu
|
||||
hw/s390x/s390-virtio-ccw.c changes from ccw_machine_9_2_class_options() moved to ccw_rhel_machine_9_6_0_class_options()
|
||||
|
||||
commit dfcee1ea4c52ac60e0a06221eafb7b6253eb10c3
|
||||
Author: Matthew Rosato <mjrosato@linux.ibm.com>
|
||||
Date: Wed Feb 26 16:00:12 2025 -0500
|
||||
|
||||
s390x/pci: add support for guests that request direct mapping
|
||||
|
||||
When receiving a guest mpcifc(4) or mpcifc(6) instruction without the T
|
||||
bit set, treat this as a request to perform direct mapping instead of
|
||||
address translation. In order to facilitate this, pin the entirety of
|
||||
guest memory into the host iommu.
|
||||
|
||||
Pinning for the direct mapping case is handled via vfio and its memory
|
||||
listener. Additionally, ram discard settings are inherited from vfio:
|
||||
coordinated discards (e.g. virtio-mem) are allowed while uncoordinated
|
||||
discards (e.g. virtio-balloon) are disabled.
|
||||
|
||||
Subsequent guest DMA operations are all expected to be of the format
|
||||
guest_phys+sdma, allowing them to be used as lookup into the host
|
||||
iommu table.
|
||||
|
||||
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
|
||||
Reviewed-by: David Hildenbrand <david@redhat.com>
|
||||
Message-ID: <20250226210013.238349-2-mjrosato@linux.ibm.com>
|
||||
Signed-off-by: Thomas Huth <thuth@redhat.com>
|
||||
|
||||
Signed-off-by: Christoph Schlameuss <cschlame@redhat.com>
|
||||
---
|
||||
hw/s390x/s390-pci-bus.c | 39 +++++++++++++++++++++++++++++++--
|
||||
hw/s390x/s390-pci-inst.c | 13 +++++++++--
|
||||
hw/s390x/s390-pci-vfio.c | 23 +++++++++++++++----
|
||||
hw/s390x/s390-virtio-ccw.c | 5 +++++
|
||||
include/hw/s390x/s390-pci-bus.h | 3 +++
|
||||
5 files changed, 75 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
|
||||
index 3e57d5faca..13bc02d837 100644
|
||||
--- a/hw/s390x/s390-pci-bus.c
|
||||
+++ b/hw/s390x/s390-pci-bus.c
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "hw/s390x/s390-pci-inst.h"
|
||||
#include "hw/s390x/s390-pci-kvm.h"
|
||||
#include "hw/s390x/s390-pci-vfio.h"
|
||||
+#include "hw/s390x/s390-virtio-ccw.h"
|
||||
+#include "hw/boards.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
@@ -724,12 +726,42 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu)
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
+void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu)
|
||||
+{
|
||||
+ MachineState *ms = MACHINE(qdev_get_machine());
|
||||
+ S390CcwMachineState *s390ms = S390_CCW_MACHINE(ms);
|
||||
+
|
||||
+ /*
|
||||
+ * For direct-mapping we must map the entire guest address space. Rather
|
||||
+ * than using an iommu, create a memory region alias that maps GPA X to
|
||||
+ * IOVA X + SDMA. VFIO will handle pinning via its memory listener.
|
||||
+ */
|
||||
+ g_autofree char *name = g_strdup_printf("iommu-dm-s390-%04x",
|
||||
+ iommu->pbdev->uid);
|
||||
+
|
||||
+ iommu->dm_mr = g_malloc0(sizeof(*iommu->dm_mr));
|
||||
+ memory_region_init_alias(iommu->dm_mr, OBJECT(&iommu->mr), name,
|
||||
+ get_system_memory(), 0,
|
||||
+ s390_get_memory_limit(s390ms));
|
||||
+ iommu->enabled = true;
|
||||
+ memory_region_add_subregion(&iommu->mr, iommu->pbdev->zpci_fn.sdma,
|
||||
+ iommu->dm_mr);
|
||||
+}
|
||||
+
|
||||
void s390_pci_iommu_disable(S390PCIIOMMU *iommu)
|
||||
{
|
||||
iommu->enabled = false;
|
||||
g_hash_table_remove_all(iommu->iotlb);
|
||||
- memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr));
|
||||
- object_unparent(OBJECT(&iommu->iommu_mr));
|
||||
+ if (iommu->dm_mr) {
|
||||
+ memory_region_del_subregion(&iommu->mr, iommu->dm_mr);
|
||||
+ object_unparent(OBJECT(iommu->dm_mr));
|
||||
+ g_free(iommu->dm_mr);
|
||||
+ iommu->dm_mr = NULL;
|
||||
+ } else {
|
||||
+ memory_region_del_subregion(&iommu->mr,
|
||||
+ MEMORY_REGION(&iommu->iommu_mr));
|
||||
+ object_unparent(OBJECT(&iommu->iommu_mr));
|
||||
+ }
|
||||
}
|
||||
|
||||
static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn)
|
||||
@@ -1130,6 +1162,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
/* Always intercept emulated devices */
|
||||
pbdev->interp = false;
|
||||
pbdev->forwarding_assist = false;
|
||||
+ pbdev->rtr_avail = false;
|
||||
}
|
||||
|
||||
if (s390_pci_msix_init(pbdev) && !pbdev->interp) {
|
||||
@@ -1488,6 +1521,8 @@ static Property s390_pci_device_properties[] = {
|
||||
DEFINE_PROP_BOOL("interpret", S390PCIBusDevice, interp, true),
|
||||
DEFINE_PROP_BOOL("forwarding-assist", S390PCIBusDevice, forwarding_assist,
|
||||
true),
|
||||
+ DEFINE_PROP_BOOL("relaxed-translation", S390PCIBusDevice, rtr_avail,
|
||||
+ true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
|
||||
index 30149546c0..803ebcd9b3 100644
|
||||
--- a/hw/s390x/s390-pci-inst.c
|
||||
+++ b/hw/s390x/s390-pci-inst.c
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "exec/memory.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
+#include "hw/boards.h"
|
||||
#include "hw/pci/pci_device.h"
|
||||
#include "hw/s390x/s390-pci-inst.h"
|
||||
#include "hw/s390x/s390-pci-bus.h"
|
||||
@@ -1008,17 +1009,25 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
|
||||
}
|
||||
|
||||
/* currently we only support designation type 1 with translation */
|
||||
- if (!(dt == ZPCI_IOTA_RTTO && t)) {
|
||||
+ if (t && dt != ZPCI_IOTA_RTTO) {
|
||||
error_report("unsupported ioat dt %d t %d", dt, t);
|
||||
s390_program_interrupt(env, PGM_OPERAND, ra);
|
||||
return -EINVAL;
|
||||
+ } else if (!t && !pbdev->rtr_avail) {
|
||||
+ error_report("relaxed translation not allowed");
|
||||
+ s390_program_interrupt(env, PGM_OPERAND, ra);
|
||||
+ return -EINVAL;
|
||||
}
|
||||
|
||||
iommu->pba = pba;
|
||||
iommu->pal = pal;
|
||||
iommu->g_iota = g_iota;
|
||||
|
||||
- s390_pci_iommu_enable(iommu);
|
||||
+ if (t) {
|
||||
+ s390_pci_iommu_enable(iommu);
|
||||
+ } else {
|
||||
+ s390_pci_iommu_direct_map_enable(iommu);
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
}
|
||||
diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c
|
||||
index 7dbbc76823..443e222912 100644
|
||||
--- a/hw/s390x/s390-pci-vfio.c
|
||||
+++ b/hw/s390x/s390-pci-vfio.c
|
||||
@@ -131,13 +131,28 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev,
|
||||
/* Store function type separately for type-specific behavior */
|
||||
pbdev->pft = cap->pft;
|
||||
|
||||
+ /*
|
||||
+ * If the device is a passthrough ISM device, disallow relaxed
|
||||
+ * translation.
|
||||
+ */
|
||||
+ if (pbdev->pft == ZPCI_PFT_ISM) {
|
||||
+ pbdev->rtr_avail = false;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* If appropriate, reduce the size of the supported DMA aperture reported
|
||||
- * to the guest based upon the vfio DMA limit.
|
||||
+ * to the guest based upon the vfio DMA limit. This is applicable for
|
||||
+ * devices that are guaranteed to not use relaxed translation. If the
|
||||
+ * device is capable of relaxed translation then we must advertise the
|
||||
+ * full aperture. In this case, if translation is used then we will
|
||||
+ * rely on the vfio DMA limit counting and use RPCIT CC1 / status 16
|
||||
+ * to request that the guest free DMA mappings as necessary.
|
||||
*/
|
||||
- vfio_size = pbdev->iommu->max_dma_limit << TARGET_PAGE_BITS;
|
||||
- if (vfio_size > 0 && vfio_size < cap->end_dma - cap->start_dma + 1) {
|
||||
- pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1;
|
||||
+ if (!pbdev->rtr_avail) {
|
||||
+ vfio_size = pbdev->iommu->max_dma_limit << TARGET_PAGE_BITS;
|
||||
+ if (vfio_size > 0 && vfio_size < cap->end_dma - cap->start_dma + 1) {
|
||||
+ pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
|
||||
index 312e8f18aa..77a1bde71e 100644
|
||||
--- a/hw/s390x/s390-virtio-ccw.c
|
||||
+++ b/hw/s390x/s390-virtio-ccw.c
|
||||
@@ -1348,8 +1348,13 @@ static void ccw_rhel_machine_9_6_0_instance_options(MachineState *machine)
|
||||
|
||||
static void ccw_rhel_machine_9_6_0_class_options(MachineClass *mc)
|
||||
{
|
||||
+ static GlobalProperty compat[] = {
|
||||
+ { TYPE_S390_PCI_DEVICE, "relaxed-translation", "off", },
|
||||
+ };
|
||||
+
|
||||
/* NB: remember to move this line to the *latest* RHEL 9 machine */
|
||||
compat_props_add(mc->compat_props, hw_compat_rhel_9, hw_compat_rhel_9_len);
|
||||
+ compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat));
|
||||
}
|
||||
DEFINE_CCW_MACHINE_AS_LATEST(9, 6, 0);
|
||||
|
||||
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
|
||||
index 2c43ea123f..04944d4fed 100644
|
||||
--- a/include/hw/s390x/s390-pci-bus.h
|
||||
+++ b/include/hw/s390x/s390-pci-bus.h
|
||||
@@ -277,6 +277,7 @@ struct S390PCIIOMMU {
|
||||
AddressSpace as;
|
||||
MemoryRegion mr;
|
||||
IOMMUMemoryRegion iommu_mr;
|
||||
+ MemoryRegion *dm_mr;
|
||||
bool enabled;
|
||||
uint64_t g_iota;
|
||||
uint64_t pba;
|
||||
@@ -362,6 +363,7 @@ struct S390PCIBusDevice {
|
||||
bool interp;
|
||||
bool forwarding_assist;
|
||||
bool aif;
|
||||
+ bool rtr_avail;
|
||||
QTAILQ_ENTRY(S390PCIBusDevice) link;
|
||||
};
|
||||
|
||||
@@ -389,6 +391,7 @@ int pci_chsc_sei_nt2_have_event(void);
|
||||
void s390_pci_sclp_configure(SCCB *sccb);
|
||||
void s390_pci_sclp_deconfigure(SCCB *sccb);
|
||||
void s390_pci_iommu_enable(S390PCIIOMMU *iommu);
|
||||
+void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu);
|
||||
void s390_pci_iommu_disable(S390PCIIOMMU *iommu);
|
||||
void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
|
||||
uint64_t faddr, uint32_t e);
|
||||
--
|
||||
2.48.1
|
||||
|
@ -0,0 +1,72 @@
|
||||
From 13e8ddbd282da692c8199a6cb9ca847334089e29 Mon Sep 17 00:00:00 2001
|
||||
From: Christoph Schlameuss <cschlame@redhat.com>
|
||||
Date: Thu, 12 Jun 2025 11:48:41 +0200
|
||||
Subject: [PATCH 02/16] s390x/pci: indicate QEMU supports relaxed translation
|
||||
for passthrough
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
RH-Author: Christoph Schlameuss <None>
|
||||
RH-MergeRequest: 376: Draft: KVM: Performance Enhanced Refresh PCI Translation
|
||||
RH-Jira: RHEL-11430
|
||||
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
||||
RH-Acked-by: Cédric Le Goater <clg@redhat.com>
|
||||
RH-Commit: [2/2] afd514268347d0b434a60d7c6c09d20b84e5d902 (cschlame/qemu-kvm)
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-11430
|
||||
|
||||
commit d9b5dfc7122559e5b5959ecf534788b90c3dd102
|
||||
Author: Matthew Rosato <mjrosato@linux.ibm.com>
|
||||
Date: Wed Feb 26 16:00:13 2025 -0500
|
||||
|
||||
s390x/pci: indicate QEMU supports relaxed translation for passthrough
|
||||
|
||||
Specifying this bit in the guest CLP response indicates that the guest
|
||||
can optionally choose to skip translation and instead use
|
||||
identity-mapped operations.
|
||||
|
||||
Tested-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Reviewed-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
|
||||
Message-ID: <20250226210013.238349-3-mjrosato@linux.ibm.com>
|
||||
Signed-off-by: Thomas Huth <thuth@redhat.com>
|
||||
|
||||
Signed-off-by: Christoph Schlameuss <cschlame@redhat.com>
|
||||
---
|
||||
hw/s390x/s390-pci-vfio.c | 5 ++++-
|
||||
include/hw/s390x/s390-pci-clp.h | 1 +
|
||||
2 files changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c
|
||||
index 443e222912..6236ac7f1e 100644
|
||||
--- a/hw/s390x/s390-pci-vfio.c
|
||||
+++ b/hw/s390x/s390-pci-vfio.c
|
||||
@@ -238,8 +238,11 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev,
|
||||
pbdev->pci_group = s390_group_create(pbdev->zpci_fn.pfgid, start_gid);
|
||||
|
||||
resgrp = &pbdev->pci_group->zpci_group;
|
||||
+ if (pbdev->rtr_avail) {
|
||||
+ resgrp->fr |= CLP_RSP_QPCIG_MASK_RTR;
|
||||
+ }
|
||||
if (cap->flags & VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH) {
|
||||
- resgrp->fr = 1;
|
||||
+ resgrp->fr |= CLP_RSP_QPCIG_MASK_REFRESH;
|
||||
}
|
||||
resgrp->dasm = cap->dasm;
|
||||
resgrp->msia = cap->msi_addr;
|
||||
diff --git a/include/hw/s390x/s390-pci-clp.h b/include/hw/s390x/s390-pci-clp.h
|
||||
index 03b7f9ba5f..6a635d693b 100644
|
||||
--- a/include/hw/s390x/s390-pci-clp.h
|
||||
+++ b/include/hw/s390x/s390-pci-clp.h
|
||||
@@ -158,6 +158,7 @@ typedef struct ClpRspQueryPciGrp {
|
||||
#define CLP_RSP_QPCIG_MASK_NOI 0xfff
|
||||
uint16_t i;
|
||||
uint8_t version;
|
||||
+#define CLP_RSP_QPCIG_MASK_RTR 0x20
|
||||
#define CLP_RSP_QPCIG_MASK_FRAME 0x2
|
||||
#define CLP_RSP_QPCIG_MASK_REFRESH 0x1
|
||||
uint8_t fr;
|
||||
--
|
||||
2.48.1
|
||||
|
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 e72aaba2efda48e083d92e6dacfe58667bdfa958 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Fri, 9 May 2025 15:40:30 -0500
|
||||
Subject: [PATCH 15/16] tests: Add iotest mirror-sparse for recent patches
|
||||
|
||||
RH-Author: Eric Blake <eblake@redhat.com>
|
||||
RH-MergeRequest: 365: blockdev-mirror: More efficient handling of sparse mirrors
|
||||
RH-Jira: RHEL-82906 RHEL-83015
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
|
||||
RH-Commit: [13/14] 6b7792e85b81e45d11c2664349db75d905e72adf (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-82906
|
||||
Jira: https://issues.redhat.com/browse/RHEL-83015
|
||||
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.48.1
|
||||
|
@ -149,7 +149,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \
|
||||
Summary: QEMU is a machine emulator and virtualizer
|
||||
Name: qemu-kvm
|
||||
Version: 9.1.0
|
||||
Release: 23%{?rcrel}%{?dist}%{?cc_suffix}
|
||||
Release: 24%{?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)
|
||||
@ -575,6 +575,52 @@ Patch201: kvm-net-vhost-user-add-QAPI-events-to-report-connection-.patch
|
||||
Patch202: kvm-file-posix-Define-DM_MPATH_PROBE_PATHS.patch
|
||||
# For RHEL-95408 - Support multipath failover with scsi-block [rhel-9]
|
||||
Patch203: kvm-file-posix-Probe-paths-and-retry-SG_IO-on-potential-.patch
|
||||
# For RHEL-11430 - [IBM 9.7 FEAT] KVM: Performance Enhanced Refresh PCI Translation - qemu part
|
||||
Patch204: kvm-s390x-pci-add-support-for-guests-that-request-direct.patch
|
||||
# For RHEL-11430 - [IBM 9.7 FEAT] KVM: Performance Enhanced Refresh PCI Translation - qemu part
|
||||
Patch205: kvm-s390x-pci-indicate-QEMU-supports-relaxed-translation.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch206: kvm-block-Expand-block-status-mode-from-bool-to-flags.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch207: kvm-file-posix-gluster-Handle-zero-block-status-hint-bet.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch208: kvm-block-Let-bdrv_co_is_zero_fast-consolidate-adjacent-.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch209: kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch210: kvm-iotests-Improve-iotest-194-to-mirror-data.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch211: kvm-mirror-Minor-refactoring.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch212: kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch213: kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch214: kvm-mirror-Drop-redundant-zero_target-parameter.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch215: kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch216: kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch217: kvm-iotests-common.rc-add-disk_usage-function.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch218: kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch
|
||||
# For RHEL-82906 - --migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7]
|
||||
# For RHEL-83015 - Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7]
|
||||
Patch219: kvm-mirror-Reduce-I-O-when-destination-is-detect-zeroes-.patch
|
||||
|
||||
%if %{have_clang}
|
||||
BuildRequires: clang
|
||||
@ -1650,6 +1696,30 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Jun 16 2025 Jon Maloy <jmaloy@redhat.com> - 9.1.0-24
|
||||
- kvm-s390x-pci-add-support-for-guests-that-request-direct.patch [RHEL-11430]
|
||||
- kvm-s390x-pci-indicate-QEMU-supports-relaxed-translation.patch [RHEL-11430]
|
||||
- kvm-block-Expand-block-status-mode-from-bool-to-flags.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-file-posix-gluster-Handle-zero-block-status-hint-bet.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-block-Let-bdrv_co_is_zero_fast-consolidate-adjacent-.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-block-Add-new-bdrv_co_is_all_zeroes-function.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-iotests-Improve-iotest-194-to-mirror-data.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-mirror-Minor-refactoring.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-mirror-Pass-full-sync-mode-rather-than-bool-to-inter.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-mirror-Allow-QMP-override-to-declare-target-already-.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-mirror-Drop-redundant-zero_target-parameter.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-mirror-Skip-pre-zeroing-destination-if-it-is-already.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-mirror-Skip-writing-zeroes-when-target-is-already-ze.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-iotests-common.rc-add-disk_usage-function.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-tests-Add-iotest-mirror-sparse-for-recent-patches.patch [RHEL-82906 RHEL-83015]
|
||||
- kvm-mirror-Reduce-I-O-when-destination-is-detect-zeroes-.patch [RHEL-82906 RHEL-83015]
|
||||
- Resolves: RHEL-11430
|
||||
([IBM 9.7 FEAT] KVM: Performance Enhanced Refresh PCI Translation - qemu part)
|
||||
- Resolves: RHEL-82906
|
||||
(--migrate-disks-detect-zeroes doesn't take effect for disk migration [rhel-9.7])
|
||||
- Resolves: RHEL-83015
|
||||
(Disk size of target raw image is full allocated when doing mirror with default discard value [rhel-9.7])
|
||||
|
||||
* Mon Jun 09 2025 Jon Maloy <jmaloy@redhat.com> - 9.1.0-23
|
||||
- kvm-net-vhost-user-add-QAPI-events-to-report-connection-.patch [RHEL-95120]
|
||||
- kvm-file-posix-Define-DM_MPATH_PROBE_PATHS.patch [RHEL-95408]
|
||||
|
Loading…
Reference in New Issue
Block a user