render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
Blob Blame History Raw
From 7e60e728bc225b3499a3d1fad6b56d0ce8c40908 Mon Sep 17 00:00:00 2001
Message-Id: <7e60e728bc225b3499a3d1fad6b56d0ce8c40908@dist-git>
From: Peter Krempa <pkrempa@redhat.com>
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 <pkrempa@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit cc7868a8b3cfa4a0062936c23e82e4a31923f724)

https://bugzilla.redhat.com/show_bug.cgi?id=1803092
Message-Id: <d37e660b9610d7a1cc42892ff3305cb477a8f98a.1584391726.git.pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
---
 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