return ret;
}
-static int qemuDomainSnapshotIsAllowed(virDomainObjPtr vm)
+static bool
+qemuDomainSnapshotIsAllowed(virDomainObjPtr vm)
{
int i;
qemuReportError(VIR_ERR_OPERATION_INVALID,
_("Disk '%s' does not support snapshotting"),
vm->def->disks[i]->src);
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
static int
static int
qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def,
- bool allow_reuse)
+ unsigned int *flags)
{
int ret = -1;
int i;
bool found = false;
bool active = virDomainObjIsActive(vm);
struct stat st;
+ bool allow_reuse = (*flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT) != 0;
+ bool atomic = (*flags & VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC) != 0;
+ int external = 0;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
for (i = 0; i < def->ndisks; i++) {
virDomainSnapshotDiskDefPtr disk = &def->disks[i];
goto cleanup;
}
found = true;
+ external++;
break;
case VIR_DOMAIN_DISK_SNAPSHOT_NO:
"selected for snapshot"));
goto cleanup;
}
+ if (active) {
+ if (external == 1 ||
+ qemuCapsGet(priv->qemuCaps, QEMU_CAPS_TRANSACTION)) {
+ *flags |= VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC;
+ } else if (atomic && external > 1) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("atomic live snapshot of multiple disks "
+ "is unsupported"));
+ goto cleanup;
+ }
+ }
ret = 0;
int i;
bool persist = false;
int thaw = 0; /* 1 if freeze succeeded, -1 if freeze failed */
+ bool atomic = (flags & VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC) != 0;
if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0)
return -1;
}
}
- if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
- /* In qemu, snapshot_blkdev on a single disk will pause cpus,
- * but this confuses libvirt since notifications are not given
- * when qemu resumes. And for multiple disks, libvirt must
- * pause externally to get all snapshots to be at the same
- * point in time. For simplicitly, we always pause ourselves
- * rather than relying on qemu doing pause.
- */
+ /* For multiple disks, libvirt must pause externally to get all
+ * snapshots to be at the same point in time, unless qemu supports
+ * transactions. For a single disk, snapshot is atomic without
+ * requiring a pause. Thanks to qemuDomainSnapshotDiskPrepare, if
+ * we got to this point, the atomic flag now says whether we need
+ * to pause, and a capability bit says whether to use transaction.
+ */
+ if (!atomic && virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
if (qemuProcessStopCPUs(driver, vm, VIR_DOMAIN_PAUSED_SAVE,
QEMU_ASYNC_JOB_NONE) < 0)
goto cleanup;
ret = -1;
}
if (vm && (qemuDomainObjEndJob(driver, vm) == 0)) {
- /* Only possible if a transient vm quit while our locks were down,
- * in which case we don't want to save snapshot metadata. */
- *vmptr = NULL;
- ret = -1;
+ /* Only possible if a transient vm quit while our locks were down,
+ * in which case we don't want to save snapshot metadata. */
+ *vmptr = NULL;
+ ret = -1;
}
return ret;
VIR_DOMAIN_SNAPSHOT_CREATE_HALT |
VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY |
VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT |
- VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE, NULL);
+ VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE |
+ VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC, NULL);
if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE) &&
!(flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY)) {
goto cleanup;
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) {
- bool allow_reuse;
-
- allow_reuse = (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT) != 0;
if (virDomainSnapshotAlignDisks(def,
VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL,
false) < 0)
goto cleanup;
- if (qemuDomainSnapshotDiskPrepare(vm, def, allow_reuse) < 0)
+ if (qemuDomainSnapshotDiskPrepare(vm, def, &flags) < 0)
goto cleanup;
def->state = VIR_DOMAIN_DISK_SNAPSHOT;
} else {