* 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
|
Summary: QEMU is a machine emulator and virtualizer
|
||||||
Name: qemu-kvm
|
Name: qemu-kvm
|
||||||
Version: 9.1.0
|
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 because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
|
||||||
# Epoch 15 used for RHEL 8
|
# Epoch 15 used for RHEL 8
|
||||||
# Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5)
|
# 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
|
Patch202: kvm-file-posix-Define-DM_MPATH_PROBE_PATHS.patch
|
||||||
# For RHEL-95408 - Support multipath failover with scsi-block [rhel-9]
|
# For RHEL-95408 - Support multipath failover with scsi-block [rhel-9]
|
||||||
Patch203: kvm-file-posix-Probe-paths-and-retry-SG_IO-on-potential-.patch
|
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}
|
%if %{have_clang}
|
||||||
BuildRequires: clang
|
BuildRequires: clang
|
||||||
@ -1650,6 +1696,30 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%changelog
|
%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
|
* 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-net-vhost-user-add-QAPI-events-to-report-connection-.patch [RHEL-95120]
|
||||||
- kvm-file-posix-Define-DM_MPATH_PROBE_PATHS.patch [RHEL-95408]
|
- kvm-file-posix-Define-DM_MPATH_PROBE_PATHS.patch [RHEL-95408]
|
||||||
|
Loading…
Reference in New Issue
Block a user