|
|
a41c76 |
From e7b1604c5848861db0896b3c7869c0ad1d971084 Mon Sep 17 00:00:00 2001
|
|
|
a41c76 |
Message-Id: <e7b1604c5848861db0896b3c7869c0ad1d971084@dist-git>
|
|
|
a41c76 |
From: Peter Krempa <pkrempa@redhat.com>
|
|
|
a41c76 |
Date: Tue, 23 Jun 2020 12:23:53 +0200
|
|
|
a41c76 |
Subject: [PATCH] qemu: checkpoint: Don't merge checkpoints during deletion
|
|
|
a41c76 |
MIME-Version: 1.0
|
|
|
a41c76 |
Content-Type: text/plain; charset=UTF-8
|
|
|
a41c76 |
Content-Transfer-Encoding: 8bit
|
|
|
a41c76 |
|
|
|
a41c76 |
Now that we've switched to the simple handling, the first thing that can
|
|
|
a41c76 |
be massively simplified is checkpoint deletion. We now need to only go
|
|
|
a41c76 |
through the backing chain and find the appropriately named bitmaps and
|
|
|
a41c76 |
delete them, no complex lookups or merging.
|
|
|
a41c76 |
|
|
|
a41c76 |
Note that compared to other functions this deletes the bitmap in all
|
|
|
a41c76 |
layers compared to others where we expect only exactly 1 bitmap of a
|
|
|
a41c76 |
name in the backing chain to prevent potential problems.
|
|
|
a41c76 |
|
|
|
a41c76 |
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
|
|
|
a41c76 |
Reviewed-by: Eric Blake <eblake@redhat.com>
|
|
|
a41c76 |
(cherry picked from commit 4c33c5568c3ee3c5952e5f3b217d78df95683b71)
|
|
|
a41c76 |
https://bugzilla.redhat.com/show_bug.cgi?id=1804593
|
|
|
a41c76 |
Message-Id: <aa99845392d5d5141672bf8d50655034e6783794.1592906423.git.pkrempa@redhat.com>
|
|
|
a41c76 |
Reviewed-by: Ján Tomko <jtomko@redhat.com>
|
|
|
a41c76 |
---
|
|
|
a41c76 |
src/qemu/qemu_checkpoint.c | 153 ++++++-------------------------------
|
|
|
a41c76 |
src/qemu/qemu_checkpoint.h | 1 -
|
|
|
a41c76 |
tests/qemublocktest.c | 7 +-
|
|
|
a41c76 |
3 files changed, 25 insertions(+), 136 deletions(-)
|
|
|
a41c76 |
|
|
|
a41c76 |
diff --git a/src/qemu/qemu_checkpoint.c b/src/qemu/qemu_checkpoint.c
|
|
|
a41c76 |
index 9762a3eb9d..b44ff05c32 100644
|
|
|
a41c76 |
--- a/src/qemu/qemu_checkpoint.c
|
|
|
a41c76 |
+++ b/src/qemu/qemu_checkpoint.c
|
|
|
a41c76 |
@@ -105,140 +105,41 @@ qemuCheckpointWriteMetadata(virDomainObjPtr vm,
|
|
|
a41c76 |
}
|
|
|
a41c76 |
|
|
|
a41c76 |
|
|
|
a41c76 |
-/**
|
|
|
a41c76 |
- * qemuCheckpointFindActiveDiskInParent:
|
|
|
a41c76 |
- * @vm: domain object
|
|
|
a41c76 |
- * @from: starting moment object
|
|
|
a41c76 |
- * @diskname: name (target) of the disk to find
|
|
|
a41c76 |
- *
|
|
|
a41c76 |
- * Find the first checkpoint starting from @from continuing through parents
|
|
|
a41c76 |
- * of the checkpoint which describes disk @diskname. Return the pointer to the
|
|
|
a41c76 |
- * definition of the disk.
|
|
|
a41c76 |
- */
|
|
|
a41c76 |
-static virDomainCheckpointDiskDef *
|
|
|
a41c76 |
-qemuCheckpointFindActiveDiskInParent(virDomainObjPtr vm,
|
|
|
a41c76 |
- virDomainMomentObjPtr from,
|
|
|
a41c76 |
- const char *diskname)
|
|
|
a41c76 |
-{
|
|
|
a41c76 |
- virDomainMomentObjPtr parent = from;
|
|
|
a41c76 |
- virDomainCheckpointDefPtr parentdef = NULL;
|
|
|
a41c76 |
- size_t i;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- while (parent) {
|
|
|
a41c76 |
- parentdef = virDomainCheckpointObjGetDef(parent);
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- for (i = 0; i < parentdef->ndisks; i++) {
|
|
|
a41c76 |
- virDomainCheckpointDiskDef *chkdisk = &parentdef->disks[i];
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- if (STRNEQ(chkdisk->name, diskname))
|
|
|
a41c76 |
- continue;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- /* currently inspected checkpoint doesn't describe the disk,
|
|
|
a41c76 |
- * continue into parent checkpoint */
|
|
|
a41c76 |
- if (chkdisk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
|
|
|
a41c76 |
- break;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- return chkdisk;
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- parent = virDomainCheckpointFindByName(vm->checkpoints,
|
|
|
a41c76 |
- parentdef->parent.parent_name);
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- return NULL;
|
|
|
a41c76 |
-}
|
|
|
a41c76 |
-
|
|
|
a41c76 |
-
|
|
|
a41c76 |
int
|
|
|
a41c76 |
qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src,
|
|
|
a41c76 |
virHashTablePtr blockNamedNodeData,
|
|
|
a41c76 |
const char *delbitmap,
|
|
|
a41c76 |
- const char *parentbitmap,
|
|
|
a41c76 |
virJSONValuePtr actions,
|
|
|
a41c76 |
const char *diskdst,
|
|
|
a41c76 |
GSList **reopenimages)
|
|
|
a41c76 |
{
|
|
|
a41c76 |
- virStorageSourcePtr n = src;
|
|
|
a41c76 |
+ virStorageSourcePtr n;
|
|
|
a41c76 |
+ bool found = false;
|
|
|
a41c76 |
|
|
|
a41c76 |
/* find the backing chain entry with bitmap named '@delbitmap' */
|
|
|
a41c76 |
- while (n) {
|
|
|
a41c76 |
- qemuBlockNamedNodeDataBitmapPtr tmp;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- if ((tmp = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
a41c76 |
- n, delbitmap))) {
|
|
|
a41c76 |
- break;
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- n = n->backingStore;
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- if (!n) {
|
|
|
a41c76 |
- virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
a41c76 |
- _("bitmap '%s' not found in backing chain of '%s'"),
|
|
|
a41c76 |
- delbitmap, diskdst);
|
|
|
a41c76 |
- return -1;
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- while (n) {
|
|
|
a41c76 |
- qemuBlockNamedNodeDataBitmapPtr srcbitmap;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- if (!(srcbitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
a41c76 |
- n, delbitmap)))
|
|
|
a41c76 |
- break;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- /* For the actual checkpoint deletion we will merge any bitmap into the
|
|
|
a41c76 |
- * bitmap of the parent checkpoint (@parentbitmap) or for any image
|
|
|
a41c76 |
- * where the parent checkpoint bitmap is not present we must rename
|
|
|
a41c76 |
- * the bitmap of the deleted checkpoint into the bitmap of the parent
|
|
|
a41c76 |
- * checkpoint as qemu can't currently take the allocation map and turn
|
|
|
a41c76 |
- * it into a bitmap and thus we wouldn't be able to do a backup. */
|
|
|
a41c76 |
- if (parentbitmap) {
|
|
|
a41c76 |
- qemuBlockNamedNodeDataBitmapPtr dstbitmap;
|
|
|
a41c76 |
- g_autoptr(virJSONValue) arr = NULL;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- dstbitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
a41c76 |
- n, parentbitmap);
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- if (dstbitmap) {
|
|
|
a41c76 |
- if (srcbitmap->recording && !dstbitmap->recording) {
|
|
|
a41c76 |
- if (qemuMonitorTransactionBitmapEnable(actions,
|
|
|
a41c76 |
- n->nodeformat,
|
|
|
a41c76 |
- dstbitmap->name) < 0)
|
|
|
a41c76 |
- return -1;
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- } else {
|
|
|
a41c76 |
- if (qemuMonitorTransactionBitmapAdd(actions,
|
|
|
a41c76 |
- n->nodeformat,
|
|
|
a41c76 |
- parentbitmap,
|
|
|
a41c76 |
- true,
|
|
|
a41c76 |
- !srcbitmap->recording,
|
|
|
a41c76 |
- srcbitmap->granularity) < 0)
|
|
|
a41c76 |
- return -1;
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
+ for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) {
|
|
|
a41c76 |
+ qemuBlockNamedNodeDataBitmapPtr bitmapdata;
|
|
|
a41c76 |
|
|
|
a41c76 |
- arr = virJSONValueNewArray();
|
|
|
a41c76 |
-
|
|
|
a41c76 |
- if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(arr,
|
|
|
a41c76 |
- n->nodeformat,
|
|
|
a41c76 |
- srcbitmap->name) < 0)
|
|
|
a41c76 |
- return -1;
|
|
|
a41c76 |
+ if (!(bitmapdata = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
a41c76 |
+ n, delbitmap)))
|
|
|
a41c76 |
+ continue;
|
|
|
a41c76 |
|
|
|
a41c76 |
- if (qemuMonitorTransactionBitmapMerge(actions,
|
|
|
a41c76 |
- n->nodeformat,
|
|
|
a41c76 |
- parentbitmap, &arr) < 0)
|
|
|
a41c76 |
- return -1;
|
|
|
a41c76 |
- }
|
|
|
a41c76 |
+ found = true;
|
|
|
a41c76 |
|
|
|
a41c76 |
if (qemuMonitorTransactionBitmapRemove(actions,
|
|
|
a41c76 |
n->nodeformat,
|
|
|
a41c76 |
- srcbitmap->name) < 0)
|
|
|
a41c76 |
+ bitmapdata->name) < 0)
|
|
|
a41c76 |
return -1;
|
|
|
a41c76 |
|
|
|
a41c76 |
if (n != src)
|
|
|
a41c76 |
*reopenimages = g_slist_prepend(*reopenimages, n);
|
|
|
a41c76 |
+ }
|
|
|
a41c76 |
|
|
|
a41c76 |
- n = n->backingStore;
|
|
|
a41c76 |
+ if (!found) {
|
|
|
a41c76 |
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
a41c76 |
+ _("bitmap '%s' not found in backing chain of '%s'"),
|
|
|
a41c76 |
+ delbitmap, diskdst);
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
}
|
|
|
a41c76 |
|
|
|
a41c76 |
return 0;
|
|
|
a41c76 |
@@ -247,8 +148,7 @@ qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src,
|
|
|
a41c76 |
|
|
|
a41c76 |
static int
|
|
|
a41c76 |
qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
|
|
|
a41c76 |
- virDomainCheckpointDefPtr chkdef,
|
|
|
a41c76 |
- virDomainMomentObjPtr parent)
|
|
|
a41c76 |
+ virDomainCheckpointDefPtr chkdef)
|
|
|
a41c76 |
{
|
|
|
a41c76 |
qemuDomainObjPrivatePtr priv = vm->privateData;
|
|
|
a41c76 |
virQEMUDriverPtr driver = priv->driver;
|
|
|
a41c76 |
@@ -270,8 +170,6 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
|
|
|
a41c76 |
for (i = 0; i < chkdef->ndisks; i++) {
|
|
|
a41c76 |
virDomainCheckpointDiskDef *chkdisk = &chkdef->disks[i];
|
|
|
a41c76 |
virDomainDiskDefPtr domdisk = virDomainDiskByTarget(vm->def, chkdisk->name);
|
|
|
a41c76 |
- virDomainCheckpointDiskDef *parentchkdisk = NULL;
|
|
|
a41c76 |
- const char *parentbitmap = NULL;
|
|
|
a41c76 |
|
|
|
a41c76 |
/* domdisk can be missing e.g. when it was unplugged */
|
|
|
a41c76 |
if (!domdisk)
|
|
|
a41c76 |
@@ -280,15 +178,8 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
|
|
|
a41c76 |
if (chkdisk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
|
|
|
a41c76 |
continue;
|
|
|
a41c76 |
|
|
|
a41c76 |
- /* If any ancestor checkpoint has a bitmap for the same
|
|
|
a41c76 |
- * disk, then this bitmap must be merged to the
|
|
|
a41c76 |
- * ancestor. */
|
|
|
a41c76 |
- if ((parentchkdisk = qemuCheckpointFindActiveDiskInParent(vm, parent,
|
|
|
a41c76 |
- chkdisk->name)))
|
|
|
a41c76 |
- parentbitmap = parentchkdisk->bitmap;
|
|
|
a41c76 |
-
|
|
|
a41c76 |
if (qemuCheckpointDiscardDiskBitmaps(domdisk->src, blockNamedNodeData,
|
|
|
a41c76 |
- chkdisk->bitmap, parentbitmap,
|
|
|
a41c76 |
+ chkdisk->bitmap,
|
|
|
a41c76 |
actions, domdisk->dst,
|
|
|
a41c76 |
&reopenimages) < 0)
|
|
|
a41c76 |
return -1;
|
|
|
a41c76 |
@@ -336,7 +227,6 @@ qemuCheckpointDiscard(virQEMUDriverPtr driver,
|
|
|
a41c76 |
bool update_parent,
|
|
|
a41c76 |
bool metadata_only)
|
|
|
a41c76 |
{
|
|
|
a41c76 |
- virDomainMomentObjPtr parent = NULL;
|
|
|
a41c76 |
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
|
|
a41c76 |
g_autofree char *chkFile = NULL;
|
|
|
a41c76 |
bool chkcurrent = chk == virDomainCheckpointGetCurrent(vm->checkpoints);
|
|
|
a41c76 |
@@ -352,14 +242,17 @@ qemuCheckpointDiscard(virQEMUDriverPtr driver,
|
|
|
a41c76 |
|
|
|
a41c76 |
if (!metadata_only) {
|
|
|
a41c76 |
virDomainCheckpointDefPtr chkdef = virDomainCheckpointObjGetDef(chk);
|
|
|
a41c76 |
- parent = virDomainCheckpointFindByName(vm->checkpoints,
|
|
|
a41c76 |
- chk->def->parent_name);
|
|
|
a41c76 |
- if (qemuCheckpointDiscardBitmaps(vm, chkdef, parent) < 0)
|
|
|
a41c76 |
+ if (qemuCheckpointDiscardBitmaps(vm, chkdef) < 0)
|
|
|
a41c76 |
return -1;
|
|
|
a41c76 |
}
|
|
|
a41c76 |
|
|
|
a41c76 |
if (chkcurrent) {
|
|
|
a41c76 |
+ virDomainMomentObjPtr parent = NULL;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
virDomainCheckpointSetCurrent(vm->checkpoints, NULL);
|
|
|
a41c76 |
+ parent = virDomainCheckpointFindByName(vm->checkpoints,
|
|
|
a41c76 |
+ chk->def->parent_name);
|
|
|
a41c76 |
+
|
|
|
a41c76 |
if (update_parent && parent) {
|
|
|
a41c76 |
virDomainCheckpointSetCurrent(vm->checkpoints, parent);
|
|
|
a41c76 |
if (qemuCheckpointWriteMetadata(vm, parent,
|
|
|
a41c76 |
diff --git a/src/qemu/qemu_checkpoint.h b/src/qemu/qemu_checkpoint.h
|
|
|
a41c76 |
index cf1e9e46cb..0d267a188c 100644
|
|
|
a41c76 |
--- a/src/qemu/qemu_checkpoint.h
|
|
|
a41c76 |
+++ b/src/qemu/qemu_checkpoint.h
|
|
|
a41c76 |
@@ -76,7 +76,6 @@ int
|
|
|
a41c76 |
qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src,
|
|
|
a41c76 |
virHashTablePtr blockNamedNodeData,
|
|
|
a41c76 |
const char *delbitmap,
|
|
|
a41c76 |
- const char *parentbitmap,
|
|
|
a41c76 |
virJSONValuePtr actions,
|
|
|
a41c76 |
const char *diskdst,
|
|
|
a41c76 |
GSList **reopenimages);
|
|
|
a41c76 |
diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c
|
|
|
a41c76 |
index 2f675d7e03..201e5df6b4 100644
|
|
|
a41c76 |
--- a/tests/qemublocktest.c
|
|
|
a41c76 |
+++ b/tests/qemublocktest.c
|
|
|
a41c76 |
@@ -830,7 +830,6 @@ struct testQemuCheckpointDeleteMergeData {
|
|
|
a41c76 |
const char *name;
|
|
|
a41c76 |
virStorageSourcePtr chain;
|
|
|
a41c76 |
const char *deletebitmap;
|
|
|
a41c76 |
- const char *parentbitmap;
|
|
|
a41c76 |
const char *nodedatafile;
|
|
|
a41c76 |
};
|
|
|
a41c76 |
|
|
|
a41c76 |
@@ -864,7 +863,6 @@ testQemuCheckpointDeleteMerge(const void *opaque)
|
|
|
a41c76 |
if (qemuCheckpointDiscardDiskBitmaps(data->chain,
|
|
|
a41c76 |
nodedata,
|
|
|
a41c76 |
data->deletebitmap,
|
|
|
a41c76 |
- data->parentbitmap,
|
|
|
a41c76 |
actions,
|
|
|
a41c76 |
"testdisk",
|
|
|
a41c76 |
&reopenimages) >= 0) {
|
|
|
a41c76 |
@@ -1318,19 +1316,18 @@ mymain(void)
|
|
|
a41c76 |
|
|
|
a41c76 |
TEST_BACKUP_BITMAP_CALCULATE("empty", bitmapSourceChain, "a", "empty");
|
|
|
a41c76 |
|
|
|
a41c76 |
-#define TEST_CHECKPOINT_DELETE_MERGE(testname, delbmp, parbmp, named) \
|
|
|
a41c76 |
+#define TEST_CHECKPOINT_DELETE_MERGE(testname, delbmp, named) \
|
|
|
a41c76 |
do { \
|
|
|
a41c76 |
checkpointdeletedata.name = testname; \
|
|
|
a41c76 |
checkpointdeletedata.chain = bitmapSourceChain; \
|
|
|
a41c76 |
checkpointdeletedata.deletebitmap = delbmp; \
|
|
|
a41c76 |
- checkpointdeletedata.parentbitmap = parbmp; \
|
|
|
a41c76 |
checkpointdeletedata.nodedatafile = named; \
|
|
|
a41c76 |
if (virTestRun("checkpoint delete " testname, \
|
|
|
a41c76 |
testQemuCheckpointDeleteMerge, &checkpointdeletedata) < 0) \
|
|
|
a41c76 |
ret = -1; \
|
|
|
a41c76 |
} while (0)
|
|
|
a41c76 |
|
|
|
a41c76 |
- TEST_CHECKPOINT_DELETE_MERGE("empty", "a", NULL, "empty");
|
|
|
a41c76 |
+ TEST_CHECKPOINT_DELETE_MERGE("empty", "a", "empty");
|
|
|
a41c76 |
|
|
|
a41c76 |
#define TEST_BITMAP_VALIDATE(testname, bitmap, rc) \
|
|
|
a41c76 |
do { \
|
|
|
a41c76 |
--
|
|
|
a41c76 |
2.27.0
|
|
|
a41c76 |
|