[PATCH 0/8] qemu: Show willingness to use blockdev-reopen

To break the chicken and egg problem loop between qemu and libvirt in using new features introduce experimental support for blockdev-reopen (or actually x-blockdev-reopen for the time being). This patchset adds QEMU_CAPS_BLOCKDEV_REOPEN capability which is currently not asserted until qemu stabilizes the blockdev-reopen interface but implements all the handlers to use it. This is a similar approach we used to add all of the bits required to use -blockdev with qemu. To show it's usefullnes two real problems are addressed using reopening: - Checkpoint deletion in backing chain, where we need to reopen the read-only backing images to allow modification of bitmaps. Using this approach will prevent qemu from having to introduce yet another ad-hoc interface to deal with the bitmaps. (note that checkpoints are also experimental themselves since they are part of the not-yet-finished incremental backup feature) - Late open of backing files for virDomainBlockCopy oVirt abuses a quirk in the old handling of block-copy when drive-mirror is used as qemu opens the backing images of the destination of the copy only once block-job-complete is called. Without blockdev-reopen it's impossible to replicate the old semantics as we need to install a backing file for the mirror copy and that is possible only using blockdev-reopen. (this change will stay disabled until blockdev-reopen is stabilized) There are a few other problems which this will deal with mostly related to bitmap handling which would also require ad-hoc qemu functionality otherwise. Since we have an existing interface we can show we are willing to use it to prevent wasting more engieering on qemu's side on partial solutions. This patchset applies on top of: https://www.redhat.com/archives/libvir-list/2020-February/msg01062.html It can be fetched from my repo: git fetch https://gitlab.com/pipo.sk/libvirt.git reopen-impl https://gitlab.com/pipo.sk/libvirt/-/commits/reopen-impl Note the above branch contains also patches which enable the feature and also enable incremental backup to facilitate simple testing without the need to use the qemu namespace. Successful use requires the following qemu patches: https://lists.gnu.org/archive/html/qemu-block/2020-02/msg01423.html https://lists.gnu.org/archive/html/qemu-block/2020-02/msg01467.html A qemu repo containing the above patches and patch to enable the detection done in my private brnch mentioned above can be fetched at: git fetch https://gitlab.com/pipo.sk/qemu.git bitmap-reopen https://gitlab.com/pipo.sk/qemu/-/commits/bitmap-reopen Peter Krempa (8): qemu: capabilities: Add QEMU_CAPS_BLOCKDEV_REOPEN qemu: monitor: Add handler for blockdev-reopen qemu: block: implement helpers for blockdev-reopen qemuCheckpointDiscardBitmaps: Reopen images for bitmap modifications qemuCheckpointDiscardBitmaps: Use correct field for checkpoint bitmap name qemuDomainBlockPivot: Move check prior to executing the pivot steps qemuDomainBlockCopyCommon: Record updated flags to block job qemu: blockcopy: Allow late opening of the backing chain of a shallow copy src/qemu/qemu_block.c | 121 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 14 ++++ src/qemu/qemu_capabilities.c | 1 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_checkpoint.c | 6 +- src/qemu/qemu_driver.c | 67 ++++++++++++++++--- src/qemu/qemu_monitor.c | 13 ++++ src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 21 ++++++ src/qemu/qemu_monitor_json.h | 4 ++ 10 files changed, 241 insertions(+), 10 deletions(-) -- 2.24.1

This capability will be asserted once qemu stabilizes 'blockdev-reopen'. For now we just add the capability so that we can introduce some code that will use the reopening call. This will show our willingness to adopt use of reopen and help qemu developers stabilize it. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_capabilities.c | 1 + src/qemu/qemu_capabilities.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 5548ffad14..1cb8b697e0 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -564,6 +564,7 @@ VIR_ENUM_IMPL(virQEMUCaps, /* 355 */ "query-named-block-nodes.flat", + "blockdev-reopen", ); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index ffb4206723..f7e420a006 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -545,6 +545,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 355 */ QEMU_CAPS_QMP_QUERY_NAMED_BLOCK_NODES_FLAT, /* query-named-block-nodes supports the 'flat' option */ + QEMU_CAPS_BLOCKDEV_REOPEN, /* 'blockdev-reopen' qmp command is supported */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; -- 2.24.1

Introduce the monitor code for using blockdev-reopen. For now we'll use x-blockdev-reopen so that the interactions between qemu and libvirt can be tested with the existing code. Since the usage will be guarded by the for-now unasserted capability we'll be able to change the called command when the command will be stabilized. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 13 +++++++++++++ src/qemu/qemu_monitor.h | 3 +++ src/qemu/qemu_monitor_json.c | 21 +++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ 4 files changed, 41 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c2a61ec587..3fab31aa38 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4378,6 +4378,19 @@ qemuMonitorBlockdevAdd(qemuMonitorPtr mon, } +int +qemuMonitorBlockdevReopen(qemuMonitorPtr mon, + virJSONValuePtr *props) +{ + VIR_DEBUG("props=%p (node-name=%s)", *props, + NULLSTR(virJSONValueObjectGetString(*props, "node-name"))); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONBlockdevReopen(mon, props); +} + + int qemuMonitorBlockdevDel(qemuMonitorPtr mon, const char *nodename) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 4e06447ffa..23b8d0a650 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1324,6 +1324,9 @@ int qemuMonitorBlockdevCreate(qemuMonitorPtr mon, int qemuMonitorBlockdevAdd(qemuMonitorPtr mon, virJSONValuePtr *props); +int qemuMonitorBlockdevReopen(qemuMonitorPtr mon, + virJSONValuePtr *props); + int qemuMonitorBlockdevDel(qemuMonitorPtr mon, const char *nodename); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 451e1afef5..0f968b7d36 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -8837,6 +8837,27 @@ qemuMonitorJSONBlockdevAdd(qemuMonitorPtr mon, } +int +qemuMonitorJSONBlockdevReopen(qemuMonitorPtr mon, + virJSONValuePtr *props) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + virJSONValuePtr pr = g_steal_pointer(props); + + if (!(cmd = qemuMonitorJSONMakeCommandInternal("x-blockdev-reopen", pr))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} + + int qemuMonitorJSONBlockdevDel(qemuMonitorPtr mon, const char *nodename) diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index ed48600b82..5b3bb295eb 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -602,6 +602,10 @@ int qemuMonitorJSONBlockdevAdd(qemuMonitorPtr mon, virJSONValuePtr *props) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int qemuMonitorJSONBlockdevReopen(qemuMonitorPtr mon, + virJSONValuePtr *props) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + int qemuMonitorJSONBlockdevDel(qemuMonitorPtr mon, const char *nodename) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); -- 2.24.1

Introduce a set of helpers to call blockdev-reopen in certain scenarios Libvirt will use the QMP command to turn certain members of the backing chain read-write for bitmap manipulation and we'll also want to use it to replace/install the backing chain of a qcow2 format node. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_block.c | 121 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 14 +++++ 2 files changed, 135 insertions(+) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 152c73f1bf..441b8ec07b 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -2960,3 +2960,124 @@ qemuBlockBitmapsHandleBlockcopy(virStorageSourcePtr src, return 0; } + + +/** + * qemuBlockReopen: + * @vm: domain object + * @props: JSON object used as argument of 'blockdev-reopen' + * @asyncJob: qemu async job type + * + * Monitor interaction to call 'blockdev-reopen' @props is consumed on successful + * call and set to NULL if consumed. + */ +static int +qemuBlockReopen(virDomainObjPtr vm, + virJSONValuePtr *props, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + int rc; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; + + rc = qemuMonitorBlockdevReopen(priv->mon, props); + + if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0) + return -1; + + return 0; +} + + +/** + * qemuBlockReopenFormat: + * @vm: domain object + * @src: storage source to reopen + * @asyncJob: qemu async job type + * + * Invokes the 'blockdev-reopen' command on the format layer of @src. This means + * that @src must be already properly configured for the desired outcome. The + * nodenames of @src are used to identify the specific image in qemu. This + * function enters the monitor. + */ +int +qemuBlockReopenFormat(virDomainObjPtr vm, + virStorageSourcePtr src, + qemuDomainAsyncJob asyncJob) +{ + g_autoptr(virJSONValue) reopenprops = NULL; + + /* If we are lacking the object here, qemu might have opened an image with + * a node name unknown to us */ + if (!src->backingStore) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("can't reopen image with unknown presence of backing store")); + return -1; + } + + if (!(reopenprops = qemuBlockStorageSourceGetBlockdevProps(src, src->backingStore))) + return -1; + + return qemuBlockReopen(vm, &reopenprops, asyncJob); +} + + +/** + * qemuBlockReopenReadWrite: + * @vm: domain object + * @src: storage source to reopen + * @asyncJob: qemu async job type + * + * Wrapper that reopens @src read-write. We currently depend on qemu + * reopening the storage with 'auto-read-only' enabled for us. + * After successful reopen @src's 'readonly' flag is modified. Does nothing + * if @src is already read-write. + */ +int +qemuBlockReopenReadWrite(virDomainObjPtr vm, + virStorageSourcePtr src, + qemuDomainAsyncJob asyncJob) +{ + if (!src->readonly) + return 0; + + src->readonly = false; + if (qemuBlockReopenFormat(vm, src, asyncJob) < 0) { + src->readonly = true; + return -1; + } + + return 0; +} + + +/** + * qemuBlockReopenReadOnly: + * @vm: domain object + * @src: storage source to reopen + * @asyncJob: qemu async job type + * + * Wrapper that reopens @src read-only. We currently depend on qemu + * reopening the storage with 'auto-read-only' enabled for us. + * After successful reopen @src's 'readonly' flag is modified. Does nothing + * if @src is already read-only. + */ +int +qemuBlockReopenReadOnly(virDomainObjPtr vm, + virStorageSourcePtr src, + qemuDomainAsyncJob asyncJob) +{ + if (src->readonly) + return 0; + + src->readonly = true; + if (qemuBlockReopenFormat(vm, src, asyncJob) < 0) { + src->readonly = false; + return -1; + } + + return 0; +} diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index eab0128d5d..b3d7d0f876 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -228,3 +228,17 @@ qemuBlockBitmapsHandleBlockcopy(virStorageSourcePtr src, virHashTablePtr blockNamedNodeData, bool shallow, virJSONValuePtr *actions); + +int +qemuBlockReopenFormat(virDomainObjPtr vm, + virStorageSourcePtr src, + qemuDomainAsyncJob asyncJob); + +int +qemuBlockReopenReadWrite(virDomainObjPtr vm, + virStorageSourcePtr src, + qemuDomainAsyncJob asyncJob); +int +qemuBlockReopenReadOnly(virDomainObjPtr vm, + virStorageSourcePtr src, + qemuDomainAsyncJob asyncJob); -- 2.24.1

Qemu's bitmap APIs don't reopen the appropriate images read-write for modification. It's libvirt's duty to reopen them via blockdev-reopen if we wish to modify the bitmaps. Use the new helpers to reopen the images for bitmap manipulation. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_checkpoint.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qemu/qemu_checkpoint.c b/src/qemu/qemu_checkpoint.c index a387e7dfe7..32b0ab0faf 100644 --- a/src/qemu/qemu_checkpoint.c +++ b/src/qemu/qemu_checkpoint.c @@ -299,6 +299,9 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm, if (qemuDomainStorageSourceAccessAllow(driver, vm, src, false, false) < 0) goto relabel; + if (qemuBlockReopenReadWrite(vm, src, QEMU_ASYNC_JOB_NONE) < 0) + goto relabel; + relabelimages = g_slist_prepend(relabelimages, src); } @@ -311,6 +314,7 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm, for (next = relabelimages; next; next = next->next) { virStorageSourcePtr src = next->data; + ignore_value(qemuBlockReopenReadOnly(vm, src, QEMU_ASYNC_JOB_NONE)); ignore_value(qemuDomainStorageSourceAccessAllow(driver, vm, src, true, false)); } -- 2.24.1

The code deleting checkpoints needs the name of the parent checkpoint's disk's bitmap but was using the disk alias instead. This would create wrong bitmaps after deleting some checkpoints. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_checkpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qemu/qemu_checkpoint.c b/src/qemu/qemu_checkpoint.c index 32b0ab0faf..378f6c147e 100644 --- a/src/qemu/qemu_checkpoint.c +++ b/src/qemu/qemu_checkpoint.c @@ -283,7 +283,7 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm, * ancestor. */ if ((parentchkdisk = qemuCheckpointFindActiveDiskInParent(vm, parent, chkdisk->name))) - parentbitmap = parentchkdisk->name; + parentbitmap = parentchkdisk->bitmap; if (qemuCheckpointDiscardDiskBitmaps(domdisk->src, blockNamedNodeData, chkdisk->bitmap, parentbitmap, -- 2.24.1

Move the check whether the job is already synchronised to the beginning of the function so that we don't try to do some of the steps necessary for pivoting prior to actually wanting to pivot. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 35ade1ef37..76787b6ccc 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17234,6 +17234,13 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, bool blockdev = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV); g_autoptr(virJSONValue) actions = NULL; + if (job->state != QEMU_BLOCKJOB_STATE_READY) { + virReportError(VIR_ERR_BLOCK_COPY_ACTIVE, + _("block job '%s' not ready for pivot yet"), + job->name); + return -1; + } + switch ((qemuBlockJobType) job->type) { case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_LAST: @@ -17271,13 +17278,6 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, break; } - if (job->state != QEMU_BLOCKJOB_STATE_READY) { - virReportError(VIR_ERR_BLOCK_COPY_ACTIVE, - _("block job '%s' not ready for pivot yet"), - job->name); - return -1; - } - qemuDomainObjEnterMonitor(driver, vm); if (blockdev) { int rc = 0; -- 2.24.1

On a Friday in 2020, Peter Krempa wrote:
Move the check whether the job is already synchronised to the beginning of the function so that we don't try to do some of the steps necessary for pivoting prior to actually wanting to pivot.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

For a long time we've masked out VIR_DOMAIN_BLOCK_COPY_SHALLOW if there's no backing chain for the copied disk to simplify the code. One of the refacors of caused that we no longer update the 'flags' variable just the local copies. This was okay until in ccd4228afff I started storing the job flags in the block job data. Given that we modify how we call qemu we also should modify @flags so that the correct value is recorded in the block job data. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 76787b6ccc..c3215bccca 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17915,8 +17915,10 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, } /* clear the _SHALLOW flag if there is only one layer */ - if (!virStorageSourceHasBacking(disk->src)) + if (!virStorageSourceHasBacking(disk->src)) { + flags &= ~VIR_DOMAIN_BLOCK_COPY_SHALLOW; mirror_shallow = false; + } if (qemuDomainBlockCopyCommonValidateUserMirrorBackingStore(mirror, mirror_shallow, -- 2.24.1

On a Friday in 2020, Peter Krempa wrote:
For a long time we've masked out VIR_DOMAIN_BLOCK_COPY_SHALLOW if there's no backing chain for the copied disk to simplify the code.
One of the refacors of caused that we no longer update the 'flags' variable just the local copies. This was okay until in ccd4228afff I started storing the job flags in the block job data.
Given that we modify how we call qemu we also should modify @flags so that the correct value is recorded in the block job data.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

oVirt used a quirk in the pre-blockdev semantics of drive-mirror which opened the backing chain of the mirror destination only once 'block-job-complete' was called. Our introduction of blockdev made qemu open the backing chain images right at the start of the job. This broke oVirt's usage of this API because they copy the data into the backing chain during the time the block copy job is running. Re-introduce late open of the backing chain if qemu supports blockdev-reopen as we can use that command to install the backing chain even for an existing image. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 53 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c3215bccca..b746cd92d9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17229,6 +17229,7 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, qemuBlockJobDataPtr job, virDomainDiskDefPtr disk) { + g_autoptr(qemuBlockStorageSourceChainData) chainattachdata = NULL; int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; bool blockdev = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV); @@ -17263,6 +17264,7 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, if (blockdev && !job->jobflagsmissing) { g_autoptr(virHashTable) blockNamedNodeData = NULL; bool shallow = job->jobflags & VIR_DOMAIN_BLOCK_COPY_SHALLOW; + bool reuse = job->jobflags & VIR_DOMAIN_BLOCK_COPY_REUSE_EXT; if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_NONE))) return -1; @@ -17271,6 +17273,17 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, blockNamedNodeData, shallow, &actions) < 0) return -1; + + /* Open and install the backing chain of 'mirror' late if we support + * blockdev-reopen. This is to appease oVirt that wants to copy + * data into the backing chain while the top image is being copied + * shallow */ + if (reuse && shallow && + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV_REOPEN)) { + if (!(chainattachdata = qemuBuildStorageSourceChainAttachPrepareBlockdev(disk->mirror->backingStore, + priv->qemuCaps))) + return -1; + } } break; @@ -17278,6 +17291,23 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, break; } + if (chainattachdata) { + int rc; + + qemuDomainObjEnterMonitor(driver, vm); + rc = qemuBlockStorageSourceChainAttach(priv->mon, chainattachdata); + if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0) + return -1; + + if (qemuBlockReopenFormat(vm, disk->mirror, QEMU_ASYNC_JOB_NONE) < 0) { + qemuDomainObjEnterMonitor(driver, vm); + qemuBlockStorageSourceChainDetach(priv->mon, chainattachdata); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + return -1; + return -1; + } + } + qemuDomainObjEnterMonitor(driver, vm); if (blockdev) { int rc = 0; @@ -18027,9 +18057,26 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, if (blockdev) { if (mirror_reuse) { - if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdev(mirror, - priv->qemuCaps))) - goto endjob; + /* oVirt depended on late-backing-chain-opening semantics the old + * qemu command had to copy the backing chain data while the top + * level is being copied. To restore this semantics if + * blockdev-reopen is supported defer opening of the backing chain + * of 'mirror' to the pivot step */ + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV_REOPEN)) { + g_autoptr(virStorageSource) terminator = virStorageSourceNew(); + + if (!terminator) + goto endjob; + + if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdevTop(mirror, + terminator, + priv->qemuCaps))) + goto endjob; + } else { + if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdev(mirror, + priv->qemuCaps))) + goto endjob; + } } else { if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_NONE))) goto endjob; -- 2.24.1
participants (2)
-
Ján Tomko
-
Peter Krempa