[libvirt] [PATCH 11/12] qemu: Add blockdev support for the block copy job

Peter Krempa pkrempa at redhat.com
Thu Aug 8 16:00:41 UTC 2019


Implement job handling for the block copy job (drive/blockdev-mirror)
when using -blockdev. In contrast to the previously implemented
blockjobs the block copy job introduces new images to the running qemu
instance, thus requires a bit more handling.

When copying to new images the code now makes use of blockdev-create to
format the images explicitly rather than depending on automagic qemu
behaviour.

Signed-off-by: Peter Krempa <pkrempa at redhat.com>
---
 src/qemu/qemu_blockjob.c                      | 87 +++++++++++++++++
 src/qemu/qemu_blockjob.h                      | 16 +++
 src/qemu/qemu_domain.c                        | 13 +++
 src/qemu/qemu_driver.c                        | 97 ++++++++++++++++---
 .../blockjob-blockdev-in.xml                  | 14 +++
 5 files changed, 216 insertions(+), 11 deletions(-)

diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c
index 70550d17e7..3003e9c518 100644
--- a/src/qemu/qemu_blockjob.c
+++ b/src/qemu/qemu_blockjob.c
@@ -309,6 +309,40 @@ qemuBlockJobNewCreate(virDomainObjPtr vm,
 }


+qemuBlockJobDataPtr
+qemuBlockJobDiskNewCopy(virDomainObjPtr vm,
+                        virDomainDiskDefPtr disk,
+                        virStorageSourcePtr mirror,
+                        bool shallow,
+                        bool reuse)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    VIR_AUTOUNREF(qemuBlockJobDataPtr) job = NULL;
+    VIR_AUTOFREE(char *) jobname = NULL;
+
+    if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) {
+        if (virAsprintf(&jobname, "copy-%s-%s", disk->dst, disk->src->nodeformat) < 0)
+            return NULL;
+    } else {
+        if (!(jobname = qemuAliasDiskDriveFromDisk(disk)))
+            return NULL;
+    }
+
+    if (!(job = qemuBlockJobDataNew(QEMU_BLOCKJOB_TYPE_COPY, jobname)))
+        return NULL;
+
+    job->mirrorChain = virObjectRef(mirror);
+
+    if (shallow && !reuse)
+        job->data.copy.shallownew = true;
+
+    if (qemuBlockJobRegister(job, vm, disk, true) < 0)
+        return NULL;
+
+    VIR_RETURN_PTR(job);
+}
+
+
 /**
  * qemuBlockJobDiskGetJob:
  * @disk: disk definition
@@ -1043,6 +1077,50 @@ qemuBlockJobProcessEventCompletedActiveCommit(virQEMUDriverPtr driver,
 }


+static void
+qemuBlockJobProcessEventConcludedCopyPivot(virQEMUDriverPtr driver,
+                                           virDomainObjPtr vm,
+                                           qemuBlockJobDataPtr job,
+                                           qemuDomainAsyncJob asyncJob)
+{
+    VIR_DEBUG("copy job '%s' on VM '%s' pivoted", job->name, vm->def->name);
+
+    if (!job->disk)
+        return;
+
+    /* for shallow copy without reusing external image the user can either not
+     * specify the backing chain in which case libvirt will open and use the
+     * chain the user provided or not specify a chain in which case we'll
+     * inherit the rest of the chain */
+    if (job->data.copy.shallownew &&
+        !virStorageSourceIsBacking(job->disk->mirror->backingStore))
+        VIR_STEAL_PTR(job->disk->mirror->backingStore, job->disk->src->backingStore);
+
+    qemuBlockJobRewriteConfigDiskSource(vm, job->disk, job->disk->mirror);
+
+    qemuBlockJobEventProcessConcludedRemoveChain(driver, vm, asyncJob, job->disk->src);
+    virObjectUnref(job->disk->src);
+    VIR_STEAL_PTR(job->disk->src, job->disk->mirror);
+}
+
+
+static void
+qemuBlockJobProcessEventConcludedCopyAbort(virQEMUDriverPtr driver,
+                                           virDomainObjPtr vm,
+                                           qemuBlockJobDataPtr job,
+                                           qemuDomainAsyncJob asyncJob)
+{
+    VIR_DEBUG("copy job '%s' on VM '%s' aborted", job->name, vm->def->name);
+
+    if (!job->disk)
+        return;
+
+    qemuBlockJobEventProcessConcludedRemoveChain(driver, vm, asyncJob, job->disk->mirror);
+    virObjectUnref(job->disk->mirror);
+    job->disk->mirror = NULL;
+}
+
+
 static void
 qemuBlockJobProcessEventConcludedCreate(virQEMUDriverPtr driver,
                                         virDomainObjPtr vm,
@@ -1111,6 +1189,12 @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job,
             break;

         case QEMU_BLOCKJOB_TYPE_COPY:
+            if (job->state == QEMU_BLOCKJOB_STATE_PIVOTING)
+                qemuBlockJobProcessEventConcludedCopyPivot(driver, vm, job, asyncJob);
+            else
+                qemuBlockJobProcessEventConcludedCopyAbort(driver, vm, job, asyncJob);
+            break;
+
         case QEMU_BLOCKJOB_TYPE_NONE:
         case QEMU_BLOCKJOB_TYPE_INTERNAL:
         case QEMU_BLOCKJOB_TYPE_LAST:
@@ -1138,6 +1222,9 @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job,
             break;

         case QEMU_BLOCKJOB_TYPE_COPY:
+            qemuBlockJobProcessEventConcludedCopyAbort(driver, vm, job, asyncJob);
+            break;
+
         case QEMU_BLOCKJOB_TYPE_NONE:
         case QEMU_BLOCKJOB_TYPE_INTERNAL:
         case QEMU_BLOCKJOB_TYPE_LAST:
diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h
index ff3c4a9eb7..41a5cd91f8 100644
--- a/src/qemu/qemu_blockjob.h
+++ b/src/qemu/qemu_blockjob.h
@@ -97,6 +97,14 @@ struct _qemuBlockJobCreateData {
 };


+typedef struct _qemuBlockJobCopyData qemuBlockJobCopyData;
+typedef qemuBlockJobCopyData *qemuBlockJobDataCopyPtr;
+
+struct _qemuBlockJobCopyData {
+    bool shallownew;
+};
+
+
 typedef struct _qemuBlockJobData qemuBlockJobData;
 typedef qemuBlockJobData *qemuBlockJobDataPtr;

@@ -113,6 +121,7 @@ struct _qemuBlockJobData {
         qemuBlockJobPullData pull;
         qemuBlockJobCommitData commit;
         qemuBlockJobCreateData create;
+        qemuBlockJobCopyData copy;
     } data;

     int type; /* qemuBlockJobType */
@@ -163,6 +172,13 @@ qemuBlockJobNewCreate(virDomainObjPtr vm,
                       virStorageSourcePtr chain,
                       bool storage);

+qemuBlockJobDataPtr
+qemuBlockJobDiskNewCopy(virDomainObjPtr vm,
+                        virDomainDiskDefPtr disk,
+                        virStorageSourcePtr mirror,
+                        bool shallow,
+                        bool reuse);
+
 qemuBlockJobDataPtr
 qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk)
     ATTRIBUTE_NONNULL(1);
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 8f32f8a035..364046a456 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2426,6 +2426,10 @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload,
             break;

         case QEMU_BLOCKJOB_TYPE_COPY:
+            if (job->data.copy.shallownew)
+                virBufferAddLit(&attrBuf, " shallownew='yes'");
+            break;
+
         case QEMU_BLOCKJOB_TYPE_NONE:
         case QEMU_BLOCKJOB_TYPE_INTERNAL:
         case QEMU_BLOCKJOB_TYPE_LAST:
@@ -2873,6 +2877,7 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job,
                                                  virDomainXMLOptionPtr xmlopt)
 {
     VIR_AUTOFREE(char *) createmode = NULL;
+    VIR_AUTOFREE(char *) shallownew = NULL;
     xmlNodePtr tmp;

     switch ((qemuBlockJobType) job->type) {
@@ -2922,6 +2927,14 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job,
             break;

         case QEMU_BLOCKJOB_TYPE_COPY:
+            if ((shallownew =  virXPathString("string(./@shallownew)", ctxt))) {
+                if (STRNEQ(shallownew, "yes"))
+                    goto broken;
+
+                job->data.copy.shallownew = true;
+            }
+            break;
+
         case QEMU_BLOCKJOB_TYPE_NONE:
         case QEMU_BLOCKJOB_TYPE_INTERNAL:
         case QEMU_BLOCKJOB_TYPE_LAST:
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index e358c6a1c4..261a4167b5 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -18310,7 +18310,6 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
 {
     virQEMUDriverPtr driver = conn->privateData;
     qemuDomainObjPrivatePtr priv = vm->privateData;
-    VIR_AUTOFREE(char *) device = NULL;
     virDomainDiskDefPtr disk = NULL;
     int ret = -1;
     bool need_unlink = false;
@@ -18322,6 +18321,11 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
     qemuBlockJobDataPtr job = NULL;
     VIR_AUTOUNREF(virStorageSourcePtr) mirror = mirrorsrc;
     bool blockdev = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV);
+    VIR_AUTOPTR(qemuBlockStorageSourceChainData) data = NULL;
+    VIR_AUTOPTR(qemuBlockStorageSourceChainData) crdata = NULL;
+    virStorageSourcePtr n;
+    virStorageSourcePtr mirrorBacking = NULL;
+    int rc = 0;

     /* Preliminaries: find the disk we are editing, sanity checks */
     virCheckFlags(VIR_DOMAIN_BLOCK_COPY_SHALLOW |
@@ -18351,9 +18355,6 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
     if (!(disk = qemuDomainDiskByName(vm->def, path)))
         goto endjob;

-    if (!(device = qemuAliasDiskDriveFromDisk(disk)))
-        goto endjob;
-
     if (qemuDomainDiskBlockJobIsActive(disk))
         goto endjob;

@@ -18428,7 +18429,8 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
         goto endjob;
     }

-    /* pre-create the image file */
+    /* pre-create the image file. In case when 'blockdev' is used this is
+     * required so that libvirt can properly label the image for access by qemu */
     if (!existing) {
         if (virStorageFileCreate(mirror) < 0) {
             virReportSystemError(errno, "%s", _("failed to create copy target"));
@@ -18445,6 +18447,15 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
                                          keepParentLabel) < 0)
         goto endjob;

+    /* we must initialize XML-provided chain prior to detecting to keep semantics
+     * with VM startup */
+    if (blockdev) {
+        for (n = mirror; virStorageSourceIsBacking(n); n = n->backingStore) {
+            if (qemuDomainPrepareStorageSourceBlockdev(disk, n, priv, cfg) < 0)
+                goto endjob;
+        }
+    }
+
     /* If reusing an external image that includes a backing file but the user
      * did not enumerate the chain in the XML we need to detect the chain */
     if (mirror_reuse &&
@@ -18456,18 +18467,72 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
     if (qemuDomainStorageSourceChainAccessAllow(driver, vm, mirror) < 0)
         goto endjob;

-    if (!(job = qemuBlockJobDiskNew(vm, disk, QEMU_BLOCKJOB_TYPE_COPY, device)))
+    if (blockdev) {
+        if (mirror_reuse) {
+            if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdev(mirror,
+                                                                          priv->qemuCaps)))
+                goto endjob;
+        } else {
+            if (qemuBlockStorageSourceCreateDetectSize(vm, mirror, disk->src, QEMU_ASYNC_JOB_NONE) < 0)
+                goto endjob;
+
+            if (mirror_shallow) {
+                /* if external backing store is populated we'll need to open it */
+                if (virStorageSourceHasBacking(mirror)) {
+                    if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdev(mirror->backingStore,
+                                                                                  priv->qemuCaps)))
+                        goto endjob;
+
+                    mirrorBacking = mirror->backingStore;
+                } else {
+                    /* backing store of original image will be reused, but the
+                     * new image must refer to it in the metadata */
+                    mirrorBacking = disk->src->backingStore;
+                }
+            }
+
+            if (!(crdata = qemuBuildStorageSourceChainAttachPrepareBlockdevTop(mirror,
+                                                                               priv->qemuCaps)))
+                goto endjob;
+        }
+
+        if (data) {
+            qemuDomainObjEnterMonitor(driver, vm);
+            rc = qemuBlockStorageSourceChainAttach(priv->mon, data);
+            if (qemuDomainObjExitMonitor(driver, vm) < 0)
+                goto endjob;
+
+            if (rc < 0)
+                goto endjob;
+        }
+
+        if (crdata &&
+            qemuBlockStorageSourceCreate(vm, mirror, mirrorBacking, mirror->backingStore,
+                                         crdata->srcdata[0], QEMU_ASYNC_JOB_NONE) < 0)
+            goto endjob;
+    }
+
+    if (!(job = qemuBlockJobDiskNewCopy(vm, disk, mirror, mirror_shallow, mirror_reuse)))
         goto endjob;

     disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;

     /* Actually start the mirroring */
     qemuDomainObjEnterMonitor(driver, vm);
-    /* qemuMonitorDriveMirror needs to honor the REUSE_EXT flag as specified
-     * by the user */
-    ret = qemuMonitorDriveMirror(priv->mon, device, mirror->path, format,
-                                 bandwidth, granularity, buf_size,
-                                 mirror_shallow, mirror_reuse);
+
+    if (blockdev) {
+        ret = qemuMonitorBlockdevMirror(priv->mon, job->name, true,
+                                        disk->src->nodeformat,
+                                        mirror->nodeformat, bandwidth,
+                                        granularity, buf_size, mirror_shallow);
+    } else {
+        /* qemuMonitorDriveMirror needs to honor the REUSE_EXT flag as specified
+         * by the user */
+        ret = qemuMonitorDriveMirror(priv->mon, job->name, mirror->path, format,
+                                     bandwidth, granularity, buf_size,
+                                     mirror_shallow, mirror_reuse);
+    }
+
     virDomainAuditDisk(vm, NULL, mirror, "mirror", ret >= 0);
     if (qemuDomainObjExitMonitor(driver, vm) < 0)
         ret = -1;
@@ -18484,6 +18549,16 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
     qemuBlockJobStarted(job, vm);

  endjob:
+    if (rc < 0 &&
+        virDomainObjIsActive(vm) &&
+        (data || crdata)) {
+        qemuDomainObjEnterMonitor(driver, vm);
+        if (data)
+            qemuBlockStorageSourceChainDetach(priv->mon, data);
+        if (crdata)
+            qemuBlockStorageSourceAttachRollback(priv->mon, crdata->srcdata[0]);
+        ignore_value(qemuDomainObjExitMonitor(driver, vm));
+    }
     if (need_unlink && virStorageFileUnlink(mirror) < 0)
         VIR_WARN("%s", _("unable to remove just-created copy target"));
     virStorageFileDeinit(mirror);
diff --git a/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml b/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml
index 408e9269db..67252a3729 100644
--- a/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml
+++ b/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml
@@ -260,6 +260,9 @@
         </source>
       </src>
     </blockjob>
+    <blockjob name='pull-vdc-libvirt-4321-format' type='copy' state='ready' shallownew='yes'>
+      <disk dst='vdc'/>
+    </blockjob>
     <blockjob name='pull-vdc-libvirt-9-format' type='commit' state='running'>
       <disk dst='vdc'/>
       <base node='libvirt-11-format'/>
@@ -571,6 +574,17 @@
             </backingStore>
           </backingStore>
         </backingStore>
+        <mirror type='file' file='/tmp/copy2' format='qcow2' job='copy' ready='yes'>
+          <format type='qcow2'/>
+          <source file='/tmp/copy2' index='4321'>
+            <privateData>
+              <nodenames>
+                <nodename type='storage' name='libvirt-4321-storage'/>
+                <nodename type='format' name='libvirt-4321-format'/>
+              </nodenames>
+            </privateData>
+          </source>
+        </mirror>
         <target dev='vdc' bus='virtio'/>
         <alias name='virtio-disk3'/>
         <address type='pci' domain='0x0000' bus='0x00' slot='0x0d' function='0x0'/>
-- 
2.21.0




More information about the libvir-list mailing list