[libvirt] [PATCH v3 17/20] wip: backup: Wire up qemu checkpoint commands over QMP

Eric Blake eblake at redhat.com
Thu Oct 25 19:20:18 UTC 2018


Time to actually issue the QMP transactions that create and
delete persistent checkpoints.  For create, we only need one
transaction: inside, we visit all disks affected by the
checkpoint, and create a new enabled bitmap, as well as
disabling the bitmap of the parent checkpoint (if any).  For
deletion, we need multiple calls: if the checkpoint to be
deleted is active, we must enable the parent; then we must
merge the existing checkpoint into the parent, and finally
we can delete the checkpoint.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 src/qemu/qemu_domain.c | 93 +++++++++++++++++++++++++++++-------------
 src/qemu/qemu_driver.c | 58 +++++++++++++++++++++++++-
 2 files changed, 121 insertions(+), 30 deletions(-)

diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index fec59d06b5..15722956bc 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -8399,44 +8399,79 @@ qemuDomainCheckpointDiscard(virQEMUDriverPtr driver,
     int ret = -1;
     virDomainCheckpointObjPtr parentchk = NULL;
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+    int i, j;

-    if (!metadata_only) {
-        if (!virDomainObjIsActive(vm)) {
-            virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
-                           _("cannot remove checkpoint from inactive domain"));
-            goto cleanup;
-        } else {
-            /* TODO: Implement QMP sequence to merge bitmaps */
-            // qemuDomainObjPrivatePtr priv;
-            // priv = vm->privateData;
-            // qemuDomainObjEnterMonitor(driver, vm);
-            // /* we continue on even in the face of error */
-            // qemuMonitorDeleteCheckpoint(priv->mon, chk->def->name);
-            // ignore_value(qemuDomainObjExitMonitor(driver, vm));
-        }
+    if (!metadata_only && !virDomainObjIsActive(vm)) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+                       _("cannot remove checkpoint from inactive domain"));
+        goto cleanup;
     }

     if (virAsprintf(&chkFile, "%s/%s/%s.xml", cfg->checkpointDir,
                     vm->def->name, chk->def->name) < 0)
         goto cleanup;

+    if (chk->def->parent) {
+        parentchk = virDomainCheckpointFindByName(vm->checkpoints,
+                                                  chk->def->parent);
+        if (!parentchk) {
+            VIR_WARN("missing parent checkpoint matching name '%s'",
+                     chk->def->parent);
+        }
+    }
+
+    if (!metadata_only) {
+        qemuDomainObjPrivatePtr priv = vm->privateData;
+        bool success = true;
+
+        qemuDomainObjEnterMonitor(driver, vm);
+        for (i = 0; i < chk->def->ndisks; i++) {
+            virDomainCheckpointDiskDef *disk = &chk->def->disks[i];
+
+            if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
+                continue;
+
+            if (parentchk) {
+                for (j = 0; j < parentchk->def->ndisks; j++) {
+                    virDomainCheckpointDiskDef *disk2;
+
+                    disk2 = &parentchk->def->disks[j];
+                    if (STRNEQ(disk->node, disk2->node))
+                        continue;
+                    if (chk == vm->current_checkpoint &&
+                        qemuMonitorEnableBitmap(priv->mon, disk->node,
+                                                disk2->bitmap) < 0) {
+                        success = false;
+                        break;
+                    }
+                    if (qemuMonitorMergeBitmaps(priv->mon, disk->node,
+                                                disk2->bitmap,
+                                                disk->bitmap) < 0) {
+                        success = false;
+                        break;
+                    }
+                }
+            }
+            if (qemuMonitorDeleteBitmap(priv->mon, disk->node,
+                                        disk->bitmap) < 0) {
+                success = false;
+                break;
+            }
+        }
+        if (qemuDomainObjExitMonitor(driver, vm) < 0 || !success)
+            goto cleanup;
+    }
+
     if (chk == vm->current_checkpoint) {
-        if (update_parent && chk->def->parent) {
-            parentchk = virDomainCheckpointFindByName(vm->checkpoints,
-                                                      chk->def->parent);
-            if (!parentchk) {
-                VIR_WARN("missing parent checkpoint matching name '%s'",
+        if (update_parent && parentchk) {
+            parentchk->def->current = true;
+            if (qemuDomainCheckpointWriteMetadata(vm, parentchk, driver->caps,
+                                                  driver->xmlopt,
+                                                  cfg->checkpointDir) < 0) {
+                VIR_WARN("failed to set parent checkpoint '%s' as current",
                          chk->def->parent);
-            } else {
-                parentchk->def->current = true;
-                if (qemuDomainCheckpointWriteMetadata(vm, parentchk, driver->caps,
-                                                      driver->xmlopt,
-                                                      cfg->checkpointDir) < 0) {
-                    VIR_WARN("failed to set parent checkpoint '%s' as current",
-                             chk->def->parent);
-                    parentchk->def->current = false;
-                    parentchk = NULL;
-                }
+                parentchk->def->current = false;
+                parentchk = NULL;
             }
         }
         vm->current_checkpoint = parentchk;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 2c5295d14a..236cbeb683 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -50,6 +50,7 @@
 #include "qemu_hostdev.h"
 #include "qemu_hotplug.h"
 #include "qemu_monitor.h"
+#include "qemu_monitor_json.h"
 #include "qemu_process.h"
 #include "qemu_migration.h"
 #include "qemu_migration_params.h"
@@ -16815,6 +16816,48 @@ qemuDomainCheckpointPrepare(virQEMUDriverPtr driver, virCapsPtr caps,
     return ret;
 }

+static int
+qemuDomainCheckpointAddActions(virJSONValuePtr actions,
+                               virDomainCheckpointObjPtr old_current,
+                               virDomainCheckpointDefPtr def)
+{
+    int i, j;
+    int ret = -1;
+
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainCheckpointDiskDef *disk = &def->disks[i];
+
+        if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
+            continue;
+        if (qemuMonitorJSONTransactionAdd(actions,
+                                          "block-dirty-bitmap-add",
+                                          "s:node", disk->node,
+                                          "s:name", disk->bitmap,
+                                          "b:persistent", true,
+                                          NULL) < 0)
+            goto cleanup;
+        if (old_current) {
+            for (j = 0; j < old_current->def->ndisks; j++) {
+                virDomainCheckpointDiskDef *disk2;
+
+                disk2 = &old_current->def->disks[j];
+                if (STRNEQ(disk->node, disk2->node))
+                    continue;
+                if (disk2->type == VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP &&
+                    qemuMonitorJSONTransactionAdd(actions,
+                                                  "x-block-dirty-bitmap-disable",
+                                                  "s:node", disk->node,
+                                                  "s:name", disk2->bitmap,
+                                                  NULL) < 0)
+                    goto cleanup;
+            }
+        }
+    }
+    ret = 0;
+
+ cleanup:
+    return ret;
+}

 static virDomainCheckpointPtr
 qemuDomainCheckpointCreateXML(virDomainPtr domain,
@@ -16832,6 +16875,9 @@ qemuDomainCheckpointCreateXML(virDomainPtr domain,
     virDomainCheckpointObjPtr other = NULL;
     virQEMUDriverConfigPtr cfg = NULL;
     virCapsPtr caps = NULL;
+    qemuDomainObjPrivatePtr priv;
+    virJSONValuePtr actions = NULL;
+    int ret;

     virCheckFlags(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE |
                   VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT |
@@ -16845,6 +16891,7 @@ qemuDomainCheckpointCreateXML(virDomainPtr domain,
     if (!(vm = qemuDomObjFromDomain(domain)))
         goto cleanup;

+    priv = vm->privateData;
     cfg = virQEMUDriverGetConfig(driver);

     if (virDomainCheckpointCreateXMLEnsureACL(domain->conn, vm->def) < 0)
@@ -16891,6 +16938,7 @@ qemuDomainCheckpointCreateXML(virDomainPtr domain,
     if (update_current)
         chk->def->current = true;
     if (vm->current_checkpoint) {
+        other = vm->current_checkpoint;
         if (!redefine &&
             VIR_STRDUP(chk->def->parent, vm->current_checkpoint->def->name) < 0)
                 goto endjob;
@@ -16910,7 +16958,14 @@ qemuDomainCheckpointCreateXML(virDomainPtr domain,
          * makes sense, such as checking that qemu-img recognizes the
          * checkpoint bitmap name in at least one of the domain's disks?  */
     } else {
-        /* TODO: issue QMP transaction command */
+        if (!(actions = virJSONValueNewArray()))
+            goto endjob;
+        if (qemuDomainCheckpointAddActions(actions, other, chk->def) < 0)
+            goto endjob;
+        qemuDomainObjEnterMonitor(driver, vm);
+        ret = qemuMonitorTransaction(priv->mon, &actions);
+        if (qemuDomainObjExitMonitor(driver, vm) < 0 || ret < 0)
+            goto endjob;
     }

     /* If we fail after this point, there's not a whole lot we can do;
@@ -16949,6 +17004,7 @@ qemuDomainCheckpointCreateXML(virDomainPtr domain,
     qemuDomainObjEndJob(driver, vm);

  cleanup:
+    virJSONValueFree(actions);
     virDomainObjEndAPI(&vm);
     virDomainCheckpointDefFree(def);
     VIR_FREE(xml);
-- 
2.17.2




More information about the libvir-list mailing list