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@xxxxxxxxxxxxx> --- 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