[PATCH 8/9] qemu: support instant snapshots

Nikolay Shirokovskiy nshirokovskiy at virtuozzo.com
Thu Nov 11 08:55:53 UTC 2021


Usual snapshot with memory of a running domain with first saves domain
memory to disk and then make a disk snapshot. As result we get snapshot
at moment in time much later then client asked for snapshot if domain
memory is large. So basically you need to wait several minutes or even
several tens of minutes before making guest unsafe changes.

This patch adds instant mode to snapshot with memory of a running
domain. In this mode snapshot is done almost at the moment of client
request. It does not depends of domain memory size. So client can
proceed with unsafe changes immediately (We need an event to notify
client though as snapshot API itself is still synchronous and API
will finish when domain memory will be stored to disk).

I dared to call this snapshot mode instant instead of background as it
named in QEMU. IMHO in case of libvirt API name background be confused
with asynchronous snapshot API which is not true.

Instant mode basically just stops guest CPUs, makes disks snapshot and
then start QEMU's background memory snapshot. Background here means
if guest writes some region in memory then this memory first is written
to disk so that eventually domain memory written to disk corresponds to
moment of starting background snapshot.

Note that background snapshot starts guest CPUs right after snapshot
start and do not stop CPUs after snapshot is finished unlikely to usual
memory snapshots or migration. Nevertheless
qemuSnapshotCreateActiveExternal calls qemuProcessStartCPUs in instant
mode in order to lock domain images and other tasks we do not do in
resume handler.

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy at virtuozzo.com>
---
 include/libvirt/libvirt-domain-snapshot.h |  2 +
 src/qemu/qemu_snapshot.c                  | 68 ++++++++++++++++++-----
 2 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/include/libvirt/libvirt-domain-snapshot.h b/include/libvirt/libvirt-domain-snapshot.h
index 90673ed0fb..2661ba2556 100644
--- a/include/libvirt/libvirt-domain-snapshot.h
+++ b/include/libvirt/libvirt-domain-snapshot.h
@@ -73,6 +73,8 @@ typedef enum {
                                                           running */
     VIR_DOMAIN_SNAPSHOT_CREATE_VALIDATE    = (1 << 9), /* validate the XML
                                                           against the schema */
+    VIR_DOMAIN_SNAPSHOT_CREATE_INSTANT     = (1 << 10),/* snapshot at the moment
+                                                          of call */
 } virDomainSnapshotCreateFlags;
 
 /* Take a snapshot of the current VM state */
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index b521634f2a..14c4a64d52 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -1398,6 +1398,7 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driver,
 {
     virObjectEvent *event;
     bool resume = false;
+    bool instant = false;
     int ret = -1;
     qemuDomainObjPrivate *priv = vm->privateData;
     virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap);
@@ -1442,13 +1443,25 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driver,
     if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PMSUSPENDED) {
         pmsuspended = true;
     } else if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
+        if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_INSTANT) {
+            if (!qemuMigrationCapsGet(vm, QEMU_MIGRATION_CAP_BACKGROUND_SNAPSHOT)) {
+                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                               _("Migration option 'background-snapshot'"
+                                 " is not supported by QEMU binary"));
+                goto cleanup;
+            }
+
+            instant = true;
+        }
+
         /* For full system external snapshots (those with memory), the guest
          * must pause (either by libvirt up front, or by qemu after
          * _LIVE converges). */
         if (memory)
             resume = true;
 
-        if (memory && !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_LIVE)) {
+        if (memory &&
+            (!(flags & VIR_DOMAIN_SNAPSHOT_CREATE_LIVE) || instant)) {
             if (qemuProcessStopCPUs(driver, vm, VIR_DOMAIN_PAUSED_SNAPSHOT,
                                     QEMU_ASYNC_JOB_SNAPSHOT) < 0)
                 goto cleanup;
@@ -1472,21 +1485,39 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driver,
     if (memory) {
         memory_existing = virFileExists(snapdef->memorysnapshotfile);
 
-        if (qemuSnapshotSaveMemory(driver, vm, snapdef, resume, false, cfg) < 0)
-            goto cleanup;
+        if (instant) {
+            if ((ret = qemuSnapshotCreateActiveExternalDisks(vm, snap,
+                                                             blockNamedNodeData, flags,
+                                                             QEMU_ASYNC_JOB_SNAPSHOT)) < 0)
+                goto cleanup;
 
-        /* the memory image was created, remove it on errors */
-        if (!memory_existing)
-            memory_unlink = true;
+            if (qemuSnapshotSaveMemory(driver, vm, snapdef, resume, true, cfg) < 0)
+                goto cleanup;
 
-    }
+            /* the memory image was created, remove it on errors */
+            if (!memory_existing)
+                memory_unlink = true;
+        } else {
+            if (qemuSnapshotSaveMemory(driver, vm, snapdef, resume, false, cfg) < 0)
+                goto cleanup;
 
-    /* the domain is now paused if a memory snapshot was requested */
+            /* the memory image was created, remove it on errors */
+            if (!memory_existing)
+                memory_unlink = true;
 
-    if ((ret = qemuSnapshotCreateActiveExternalDisks(vm, snap,
-                                                     blockNamedNodeData, flags,
-                                                     QEMU_ASYNC_JOB_SNAPSHOT)) < 0)
-        goto cleanup;
+            /* the domain is now paused if a memory snapshot was requested */
+            if ((ret = qemuSnapshotCreateActiveExternalDisks(vm, snap,
+                                                             blockNamedNodeData, flags,
+                                                             QEMU_ASYNC_JOB_SNAPSHOT)) < 0)
+                goto cleanup;
+        }
+    } else {
+        /* the domain is now paused if a memory snapshot was requested */
+        if ((ret = qemuSnapshotCreateActiveExternalDisks(vm, snap,
+                                                         blockNamedNodeData, flags,
+                                                         QEMU_ASYNC_JOB_SNAPSHOT)) < 0)
+            goto cleanup;
+    }
 
     /* the snapshot is complete now */
     if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT) {
@@ -1575,7 +1606,8 @@ qemuSnapshotCreateXML(virDomainPtr domain,
                   VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE |
                   VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC |
                   VIR_DOMAIN_SNAPSHOT_CREATE_LIVE |
-                  VIR_DOMAIN_SNAPSHOT_CREATE_VALIDATE, NULL);
+                  VIR_DOMAIN_SNAPSHOT_CREATE_VALIDATE |
+                  VIR_DOMAIN_SNAPSHOT_CREATE_INSTANT, NULL);
 
     VIR_REQUIRE_FLAG_RET(VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE,
                          VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY,
@@ -1584,6 +1616,16 @@ qemuSnapshotCreateXML(virDomainPtr domain,
                             VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE,
                             NULL);
 
+    VIR_EXCLUSIVE_FLAGS_RET(VIR_DOMAIN_SNAPSHOT_CREATE_INSTANT,
+                            VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY,
+                            NULL);
+    VIR_EXCLUSIVE_FLAGS_RET(VIR_DOMAIN_SNAPSHOT_CREATE_INSTANT,
+                            VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE,
+                            NULL);
+    VIR_REQUIRE_FLAG_RET(VIR_DOMAIN_SNAPSHOT_CREATE_INSTANT,
+                         VIR_DOMAIN_SNAPSHOT_CREATE_LIVE,
+                         NULL);
+
     if ((redefine && !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT)) ||
         (flags & VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA))
         update_current = false;
-- 
2.27.0




More information about the libvir-list mailing list