From 7e60e728bc225b3499a3d1fad6b56d0ce8c40908 Mon Sep 17 00:00:00 2001 Message-Id: <7e60e728bc225b3499a3d1fad6b56d0ce8c40908@dist-git> From: Peter Krempa Date: Mon, 16 Mar 2020 22:11:39 +0100 Subject: [PATCH] qemu: blockcopy: Allow late opening of the backing chain of a shallow copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 allows us to use blockdev-snapshot on write-only nodes as it can be used to install the backing chain even for an existing image now. Signed-off-by: Peter Krempa Reviewed-by: Eric Blake (cherry picked from commit cc7868a8b3cfa4a0062936c23e82e4a31923f724) https://bugzilla.redhat.com/show_bug.cgi?id=1803092 Message-Id: Reviewed-by: Ján Tomko --- src/qemu/qemu_driver.c | 57 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5a4e979907..441bb02b6b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17556,10 +17556,12 @@ 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); g_autoptr(virJSONValue) actions = NULL; + g_autoptr(virJSONValue) reopenactions = NULL; if (job->state != QEMU_BLOCKJOB_STATE_READY) { virReportError(VIR_ERR_BLOCK_COPY_ACTIVE, @@ -17590,6 +17592,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; @@ -17598,6 +17601,27 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, blockNamedNodeData, shallow, &actions) < 0) return -1; + + /* Open and install the backing chain of 'mirror' late if we can use + * blockdev-snapshot to do it. 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_SNAPSHOT_ALLOW_WRITE_ONLY) && + virStorageSourceHasBacking(disk->mirror)) { + + if (!(chainattachdata = qemuBuildStorageSourceChainAttachPrepareBlockdev(disk->mirror->backingStore, + priv->qemuCaps))) + return -1; + + reopenactions = virJSONValueNewArray(); + + if (qemuMonitorTransactionSnapshotBlockdev(reopenactions, + disk->mirror->backingStore->nodeformat, + disk->mirror->nodeformat)) + return -1; + } + } break; @@ -17609,7 +17633,15 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, if (blockdev) { int rc = 0; - if (actions) + if (chainattachdata) { + if ((rc = qemuBlockStorageSourceChainAttach(priv->mon, chainattachdata)) == 0) { + /* install backing images on success, or unplug them on failure */ + if ((rc = qemuMonitorTransaction(priv->mon, &reopenactions)) != 0) + qemuBlockStorageSourceChainDetach(priv->mon, chainattachdata); + } + } + + if (actions && rc == 0) rc = qemuMonitorTransaction(priv->mon, &actions); if (rc == 0) @@ -18354,9 +18386,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_SNAPSHOT_ALLOW_WRITE_ONLY)) { + 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.25.1