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(a)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