[libvirt] [PATCHv6 6/8] blockjob: implement block copy for qemu

Eric Blake eblake at redhat.com
Tue Apr 24 02:49:28 UTC 2012


Minimal patch to wire up all the pieces in the previous patches
to actually enable a block copy job.  By minimal, I mean that
qemu creates the file (that is, no REUSE_EXT flag support yet),
SELinux must be disabled, a lock manager is not informed, and
the audit logs aren't updated.  But those will be added as
improvements in future patches.

* src/qemu/qemu_driver.c (qemuDomainBlockCopy): New function.
(qemuDomainBlockRebase): Call it when appropriate.
---

v6: no real changes from v5

 src/qemu/qemu_driver.c |  124 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 122 insertions(+), 2 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index adbc27d..cebbf46 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -11950,10 +11950,128 @@ qemuDomainBlockJobSetSpeed(virDomainPtr dom, const char *path,
 }

 static int
+qemuDomainBlockCopy(virDomainPtr dom, const char *path,
+                    const char *dest, const char *format,
+                    unsigned long bandwidth, unsigned int flags)
+{
+    struct qemud_driver *driver = dom->conn->privateData;
+    virDomainObjPtr vm = NULL;
+    qemuDomainObjPrivatePtr priv;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    char *device = NULL;
+    virDomainDiskDefPtr disk;
+    int ret = -1;
+    int idx;
+
+    /* Preliminaries: find the disk we are editing, sanity checks */
+    virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW, -1);
+
+    qemuDriverLock(driver);
+    virUUIDFormat(dom->uuid, uuidstr);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    if (!vm) {
+        qemuReportError(VIR_ERR_NO_DOMAIN,
+                        _("no domain with matching uuid '%s'"), uuidstr);
+        goto cleanup;
+    }
+
+    device = qemuDiskPathToAlias(vm, path, &idx);
+    if (!device) {
+        goto cleanup;
+    }
+    disk = vm->def->disks[idx];
+    if (disk->mirror) {
+        qemuReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
+                        _("disk '%s' already in active block copy job"),
+                        disk->dst);
+        goto cleanup;
+    }
+
+    priv = vm->privateData;
+    /* XXX Should we add a flag that lets the user fail up front if
+     * pivot (QEMU_CAPS_DRIVE_REOPEN) is not supported, rather than
+     * making them wait until the mirroring phase to find out?  */
+    if (!(qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE_MIRROR) &&
+          qemuCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKJOB_ASYNC))) {
+        qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                        _("block copy is not supported with this QEMU binary"));
+        goto cleanup;
+    }
+    if (vm->persistent) {
+        /* XXX if qemu ever lets us start a new domain with mirroring
+         * already active, we can relax this; but for now, the risk of
+         * 'managedsave' due to libvirt-guests means we can't risk
+         * this on persistent domains.  */
+        qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                        _("domain is not transient"));
+        goto cleanup;
+    }
+
+    if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0)
+        goto cleanup;
+
+    if (!virDomainObjIsActive(vm)) {
+        qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                        _("domain is not running"));
+        goto endjob;
+    }
+
+    /* Prepare the destination file.  */
+    /* XXX We also need to add security labeling, lock manager lease,
+     * and auditing of those events, as well as to support reuse of
+     * existing images, including probing the existing format of an
+     * existing image.  */
+    if (!format)
+        format = disk->driverType;
+    if ((format && !(disk->mirrorFormat = strdup(format))) ||
+        !(disk->mirror = strdup(dest))) {
+        virReportOOMError();
+        goto endjob;
+    }
+
+    /* Actually start the mirroring */
+    qemuDomainObjEnterMonitorWithDriver(driver, vm);
+    ret = qemuMonitorDriveMirror(priv->mon, device, dest, format, flags);
+    if (ret == 0 && bandwidth != 0)
+        ret = qemuMonitorBlockJob(priv->mon, device, NULL, bandwidth, NULL,
+                                  BLOCK_JOB_SPEED_INTERNAL, true);
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+endjob:
+    if (ret < 0) {
+        VIR_FREE(disk->mirror);
+        VIR_FREE(disk->mirrorFormat);
+    }
+    if (qemuDomainObjEndJob(driver, vm) == 0) {
+        vm = NULL;
+        goto cleanup;
+    }
+
+cleanup:
+    VIR_FREE(device);
+    if (vm)
+        virDomainObjUnlock(vm);
+    qemuDriverUnlock(driver);
+    return ret;
+}
+
+static int
 qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
                       unsigned long bandwidth, unsigned int flags)
 {
-    virCheckFlags(0, -1);
+    virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
+                  VIR_DOMAIN_BLOCK_REBASE_COPY |
+                  VIR_DOMAIN_BLOCK_REBASE_COPY_RAW, -1);
+
+    if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY) {
+        const char *format = NULL;
+        if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
+            format = "raw";
+        flags &= ~(VIR_DOMAIN_BLOCK_REBASE_COPY |
+                   VIR_DOMAIN_BLOCK_REBASE_COPY_RAW);
+        return qemuDomainBlockCopy(dom, path, base, format, bandwidth, flags);
+    }
+
     return qemuDomainBlockJobImpl(dom, path, base, bandwidth, NULL,
                                   BLOCK_JOB_PULL, flags);
 }
@@ -11962,7 +12080,9 @@ static int
 qemuDomainBlockPull(virDomainPtr dom, const char *path, unsigned long bandwidth,
                     unsigned int flags)
 {
-    return qemuDomainBlockRebase(dom, path, NULL, bandwidth, flags);
+    virCheckFlags(0, -1);
+    return qemuDomainBlockJobImpl(dom, path, NULL, bandwidth, NULL,
+                                  BLOCK_JOB_PULL, flags);
 }

 static int
-- 
1.7.7.6




More information about the libvir-list mailing list