[libvirt] [PATCH v10 01/10] backup: qemu: Implement VIR_DOMAIN_CHECKPOINT_XML_SIZE flag

Eric Blake eblake at redhat.com
Thu Aug 22 01:42:40 UTC 2019


Once a checkpoint has been created, it is desirable to estimate the
size of the disk delta that is represented between the checkpoint and
the current operation. To do this, we have to scrape information out
of QMP query-block on a request from the user.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 src/qemu/qemu_monitor.h      |  4 ++
 src/qemu/qemu_monitor_json.h |  4 ++
 src/qemu/qemu_driver.c       | 38 ++++++++++++++++-
 src/qemu/qemu_monitor.c      | 11 +++++
 src/qemu/qemu_monitor_json.c | 80 ++++++++++++++++++++++++++++++++++++
 5 files changed, 136 insertions(+), 1 deletion(-)

diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 88c9702530..9d321bdeda 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -24,6 +24,7 @@
 #include "internal.h"

 #include "domain_conf.h"
+#include "checkpoint_conf.h"
 #include "virbitmap.h"
 #include "virhash.h"
 #include "virjson.h"
@@ -678,6 +679,9 @@ int qemuMonitorBlockStatsUpdateCapacity(qemuMonitorPtr mon,
 int qemuMonitorBlockStatsUpdateCapacityBlockdev(qemuMonitorPtr mon,
                                                 virHashTablePtr stats)
     ATTRIBUTE_NONNULL(2);
+int qemuMonitorUpdateCheckpointSize(qemuMonitorPtr mon,
+                                    virDomainCheckpointDefPtr chk)
+    ATTRIBUTE_NONNULL(2);

 int qemuMonitorBlockResize(qemuMonitorPtr mon,
                            const char *device,
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 61e64e831b..3815af6d2b 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -97,6 +97,10 @@ int qemuMonitorJSONBlockResize(qemuMonitorPtr mon,
                                const char *nodename,
                                unsigned long long size);

+int qemuMonitorJSONUpdateCheckpointSize(qemuMonitorPtr mon,
+                                        virDomainCheckpointDefPtr chk,
+                                        virDomainObjPtr vm);
+
 int qemuMonitorJSONSetPassword(qemuMonitorPtr mon,
                                const char *protocol,
                                const char *password,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 11f97dbc65..ff444ceecd 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -17420,11 +17420,14 @@ qemuDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint,
     virDomainObjPtr vm = NULL;
     char *xml = NULL;
     virDomainMomentObjPtr chk = NULL;
+    qemuDomainObjPrivatePtr priv;
+    int rc;
     virDomainCheckpointDefPtr chkdef;
     unsigned int format_flags;

     virCheckFlags(VIR_DOMAIN_CHECKPOINT_XML_SECURE |
-                  VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN, NULL);
+                  VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN |
+                  VIR_DOMAIN_CHECKPOINT_XML_SIZE, NULL);

     if (!(vm = qemuDomObjFromCheckpoint(checkpoint)))
         return NULL;
@@ -17436,10 +17439,43 @@ qemuDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint,
         goto cleanup;
     chkdef = virDomainCheckpointObjGetDef(chk);

+    if (flags & VIR_DOMAIN_CHECKPOINT_XML_SIZE) {
+        /* TODO: for non-current checkpoint, this requires a QMP sequence per
+           disk, since the stat of one bitmap in isolation is too low,
+           and merely adding bitmap sizes may be too high:
+             block-dirty-bitmap-create tmp
+             for each bitmap from checkpoint to current:
+               add bitmap to src_list
+             block-dirty-bitmap-merge dst=tmp src_list
+             query-block and read tmp size
+             block-dirty-bitmap-remove tmp
+           So for now, go with simpler query-blocks only for current.
+        */
+        if (virDomainCheckpointGetCurrent(vm->checkpoints) != chk) {
+            virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                           _("cannot compute size for non-current checkpoint '%s'"),
+                           checkpoint->name);
+            goto cleanup;
+        }
+
+        if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
+            goto cleanup;
+
+        if (virDomainObjCheckActive(vm) < 0)
+            goto endjob;
+
+        priv = vm->privateData;
+        qemuDomainObjEnterMonitor(driver, vm);
+        rc = qemuMonitorUpdateCheckpointSize(priv->mon, chkdef);
+        if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
+            goto endjob;
+    }
+
     format_flags = virDomainCheckpointFormatConvertXMLFlags(flags);
     xml = virDomainCheckpointDefFormat(chkdef, driver->caps, driver->xmlopt,
                                        format_flags);

+ endjob:
     if (flags & VIR_DOMAIN_CHECKPOINT_XML_SIZE)
         qemuDomainObjEndJob(driver, vm);

diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index a880da3ab6..912ff677f2 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -2321,6 +2321,17 @@ qemuMonitorBlockStatsUpdateCapacityBlockdev(qemuMonitorPtr mon,
     return qemuMonitorJSONBlockStatsUpdateCapacityBlockdev(mon, stats);
 }

+/* Updates "chk" to fill in size of the associated bitmap */
+int qemuMonitorUpdateCheckpointSize(qemuMonitorPtr mon,
+                                    virDomainCheckpointDefPtr chk)
+{
+    VIR_DEBUG("chk=%p", chk);
+
+    QEMU_CHECK_MONITOR(mon);
+
+    return qemuMonitorJSONUpdateCheckpointSize(mon, chk, mon->vm);
+}
+
 int
 qemuMonitorBlockResize(qemuMonitorPtr mon,
                        const char *device,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index d38b2f2cbe..0dadcce609 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -2926,6 +2926,86 @@ int qemuMonitorJSONBlockResize(qemuMonitorPtr mon,
     return ret;
 }

+int qemuMonitorJSONUpdateCheckpointSize(qemuMonitorPtr mon,
+                                        virDomainCheckpointDefPtr chk,
+                                        virDomainObjPtr vm)
+{
+    int ret = -1;
+    size_t i, j;
+    virJSONValuePtr devices;
+
+    if (!(devices = qemuMonitorJSONQueryBlock(mon)))
+        return -1;
+
+    for (i = 0; i < virJSONValueArraySize(devices); i++) {
+        virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
+        virJSONValuePtr inserted;
+        virJSONValuePtr bitmaps = NULL;
+        const char *node;
+        virDomainCheckpointDiskDefPtr disk;
+
+        if (!(dev = qemuMonitorJSONGetBlockDev(devices, i)))
+            goto cleanup;
+
+        if (!(inserted = virJSONValueObjectGetObject(dev, "inserted")))
+            continue;
+        if (!(node = virJSONValueObjectGetString(inserted, "node-name"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("query-block device entry was not in expected format"));
+            goto cleanup;
+        }
+
+        for (j = 0; j < chk->ndisks; j++) {
+            const char *expect_node;
+
+            disk = &chk->disks[j];
+            if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
+                continue;
+            expect_node = qemuDomainDiskNodeFormatLookup(vm, disk->name);
+            if (STREQ(expect_node, node))
+                break;
+        }
+        if (j == chk->ndisks) {
+            VIR_DEBUG("query-block did not find node %s", node);
+            continue;
+        }
+        if (!(bitmaps = virJSONValueObjectGetArray(dev, "dirty-bitmaps"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("disk %s dirty bitmaps missing"), disk->name);
+            goto cleanup;
+        }
+        for (j = 0; j < virJSONValueArraySize(bitmaps); j++) {
+            virJSONValuePtr map = virJSONValueArrayGet(bitmaps, j);
+            const char *name;
+
+            if (!(name = virJSONValueObjectGetString(map, "name"))) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("dirty bitmaps entry was not in expected format"));
+                goto cleanup;
+            }
+            if (STRNEQ(name, disk->bitmap))
+                continue;
+            if (virJSONValueObjectGetNumberUlong(map, "count", &disk->size) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("invalid bitmap count"));
+                goto cleanup;
+            }
+            break;
+        }
+        if (j == virJSONValueArraySize(bitmaps)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("disk %s dirty bitmap info missing"), disk->name);
+            goto cleanup;
+        }
+    }
+
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(devices);
+    return ret;
+}
+

 int qemuMonitorJSONSetPassword(qemuMonitorPtr mon,
                                const char *protocol,
-- 
2.21.0




More information about the libvir-list mailing list