Blame SOURCES/libvirt-qemu-backup-Rewrite-backup-bitmap-handling-to-the-new-bitmap-semantics.patch

a41c76
From 1fd01969c2a7c4deefc28363c9748786ca5d7169 Mon Sep 17 00:00:00 2001
a41c76
Message-Id: <1fd01969c2a7c4deefc28363c9748786ca5d7169@dist-git>
a41c76
From: Peter Krempa <pkrempa@redhat.com>
a41c76
Date: Tue, 23 Jun 2020 12:23:57 +0200
a41c76
Subject: [PATCH] qemu: backup: Rewrite backup bitmap handling to the new
a41c76
 bitmap semantics
a41c76
MIME-Version: 1.0
a41c76
Content-Type: text/plain; charset=UTF-8
a41c76
Content-Transfer-Encoding: 8bit
a41c76
a41c76
Reuse qemuBlockGetBitmapMergeActions which allows removal of the ad-hoc
a41c76
implementation of bitmap merging for backup. The new approach is simpler
a41c76
and also more robust in case some of the bitmaps break as they remove
a41c76
the dependency on the whole chain of bitmaps working.
a41c76
a41c76
The new approach also allows backups if a snapshot is created outside of
a41c76
libvirt.
a41c76
a41c76
Additionally the code is greatly simplified.
a41c76
a41c76
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
a41c76
Reviewed-by: Eric Blake <eblake@redhat.com>
a41c76
(cherry picked from commit e0d8d989e2bc9024f85b676166da0305c4b4014d)
a41c76
https://bugzilla.redhat.com/show_bug.cgi?id=1804593
a41c76
Message-Id: <468adeee4056441afbf7b452def02179da043030.1592906423.git.pkrempa@redhat.com>
a41c76
Reviewed-by: Ján Tomko <jtomko@redhat.com>
a41c76
---
a41c76
 src/qemu/qemu_backup.c                        | 220 +++---------------
a41c76
 src/qemu/qemu_backup.h                        |  12 +-
a41c76
 tests/qemublocktest.c                         |  86 ++-----
a41c76
 .../backupmerge/empty-out.json                |   4 +-
a41c76
 4 files changed, 63 insertions(+), 259 deletions(-)
a41c76
a41c76
diff --git a/src/qemu/qemu_backup.c b/src/qemu/qemu_backup.c
a41c76
index 13e2a7412b..67d646f477 100644
a41c76
--- a/src/qemu/qemu_backup.c
a41c76
+++ b/src/qemu/qemu_backup.c
a41c76
@@ -173,199 +173,58 @@ qemuBackupDiskDataCleanup(virDomainObjPtr vm,
a41c76
 }
a41c76
 
a41c76
 
a41c76
-/**
a41c76
- * qemuBackupBeginCollectIncrementalCheckpoints:
a41c76
- * @vm: domain object
a41c76
- * @incrFrom: name of checkpoint representing starting point of incremental backup
a41c76
- *
a41c76
- * Returns a NULL terminated list of pointers to checkpoint definitions in
a41c76
- * chronological order starting from the 'current' checkpoint until reaching
a41c76
- * @incrFrom.
a41c76
- */
a41c76
-static virDomainMomentDefPtr *
a41c76
-qemuBackupBeginCollectIncrementalCheckpoints(virDomainObjPtr vm,
a41c76
-                                             const char *incrFrom)
a41c76
-{
a41c76
-    virDomainMomentObjPtr n = virDomainCheckpointGetCurrent(vm->checkpoints);
a41c76
-    g_autofree virDomainMomentDefPtr *incr = NULL;
a41c76
-    size_t nincr = 0;
a41c76
-
a41c76
-    while (n) {
a41c76
-        virDomainMomentDefPtr def = n->def;
a41c76
-
a41c76
-        if (VIR_APPEND_ELEMENT_COPY(incr, nincr, def) < 0)
a41c76
-            return NULL;
a41c76
-
a41c76
-        if (STREQ(def->name, incrFrom)) {
a41c76
-            def = NULL;
a41c76
-            if (VIR_APPEND_ELEMENT_COPY(incr, nincr, def) < 0)
a41c76
-                return NULL;
a41c76
-
a41c76
-            return g_steal_pointer(&incr;;
a41c76
-        }
a41c76
-
a41c76
-        if (!n->def->parent_name)
a41c76
-            break;
a41c76
-
a41c76
-        n = virDomainCheckpointFindByName(vm->checkpoints, n->def->parent_name);
a41c76
-    }
a41c76
-
a41c76
-    virReportError(VIR_ERR_OPERATION_INVALID,
a41c76
-                   _("could not locate checkpoint '%s' for incremental backup"),
a41c76
-                   incrFrom);
a41c76
-    return NULL;
a41c76
-}
a41c76
-
a41c76
-
a41c76
-static int
a41c76
-qemuBackupGetBitmapMergeRange(virStorageSourcePtr from,
a41c76
-                              const char *bitmapname,
a41c76
-                              virJSONValuePtr *actions,
a41c76
-                              virStorageSourcePtr *to,
a41c76
-                              const char *diskdst,
a41c76
-                              virHashTablePtr blockNamedNodeData)
a41c76
+int
a41c76
+qemuBackupDiskPrepareOneBitmapsChain(virStorageSourcePtr backingChain,
a41c76
+                                     virStorageSourcePtr targetsrc,
a41c76
+                                     const char *targetbitmap,
a41c76
+                                     const char *incremental,
a41c76
+                                     virJSONValuePtr actions,
a41c76
+                                     virHashTablePtr blockNamedNodeData)
a41c76
 {
a41c76
-    g_autoptr(virJSONValue) act = virJSONValueNewArray();
a41c76
-    virStorageSourcePtr tmpsrc = NULL;
a41c76
-    virStorageSourcePtr n;
a41c76
-    bool foundbitmap = false;
a41c76
+    g_autoptr(virJSONValue) tmpactions = NULL;
a41c76
 
a41c76
-    for (n = from; virStorageSourceIsBacking(n); n = n->backingStore) {
a41c76
-        qemuBlockNamedNodeDataBitmapPtr bitmap = NULL;
a41c76
-
a41c76
-        if (!(bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
a41c76
-                                                             n,
a41c76
-                                                             bitmapname)))
a41c76
-            break;
a41c76
-
a41c76
-        foundbitmap = true;
a41c76
-
a41c76
-        if (bitmap->inconsistent) {
a41c76
-            virReportError(VIR_ERR_INVALID_ARG,
a41c76
-                           _("bitmap '%s' for image '%s%u' is inconsistent"),
a41c76
-                           bitmap->name, diskdst, n->id);
a41c76
-            return -1;
a41c76
-        }
a41c76
-
a41c76
-        if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(act,
a41c76
-                                                             n->nodeformat,
a41c76
-                                                             bitmapname) < 0)
a41c76
-            return -1;
a41c76
-
a41c76
-        tmpsrc = n;
a41c76
-    }
a41c76
-
a41c76
-    if (!foundbitmap) {
a41c76
-        virReportError(VIR_ERR_INVALID_ARG,
a41c76
-                       _("failed to find bitmap '%s' in image '%s%u'"),
a41c76
-                       bitmapname, diskdst, from->id);
a41c76
+    if (qemuBlockGetBitmapMergeActions(backingChain, NULL, targetsrc,
a41c76
+                                       incremental, targetbitmap, NULL,
a41c76
+                                       &tmpactions,
a41c76
+                                       blockNamedNodeData) < 0)
a41c76
         return -1;
a41c76
-    }
a41c76
 
a41c76
-    *actions = g_steal_pointer(&act;;
a41c76
-    *to = tmpsrc;
a41c76
+    if (tmpactions &&
a41c76
+        virJSONValueArrayConcat(actions, tmpactions) < 0)
a41c76
+        return -1;
a41c76
 
a41c76
     return 0;
a41c76
 }
a41c76
 
a41c76
 
a41c76
-virJSONValuePtr
a41c76
-qemuBackupDiskPrepareOneBitmapsChain(virDomainMomentDefPtr *incremental,
a41c76
-                                     virStorageSourcePtr backingChain,
a41c76
-                                     virHashTablePtr blockNamedNodeData,
a41c76
-                                     const char *diskdst)
a41c76
-{
a41c76
-    g_autoptr(virJSONValue) ret = NULL;
a41c76
-    size_t incridx = 0;
a41c76
-    virStorageSourcePtr n = backingChain;
a41c76
-
a41c76
-    ret = virJSONValueNewArray();
a41c76
-
a41c76
-    for (incridx = 0; incremental[incridx]; incridx++) {
a41c76
-        g_autoptr(virJSONValue) tmp = virJSONValueNewArray();
a41c76
-        virStorageSourcePtr tmpsrc = NULL;
a41c76
-        virDomainCheckpointDefPtr chkdef = (virDomainCheckpointDefPtr) incremental[incridx];
a41c76
-        bool checkpoint_has_disk = false;
a41c76
-        size_t i;
a41c76
-
a41c76
-        for (i = 0; i < chkdef->ndisks; i++) {
a41c76
-            if (STRNEQ_NULLABLE(diskdst, chkdef->disks[i].name))
a41c76
-                continue;
a41c76
-
a41c76
-            if (chkdef->disks[i].type == VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
a41c76
-                checkpoint_has_disk = true;
a41c76
-
a41c76
-            break;
a41c76
-        }
a41c76
-
a41c76
-        if (!checkpoint_has_disk) {
a41c76
-            if (!incremental[incridx + 1]) {
a41c76
-                virReportError(VIR_ERR_INVALID_ARG,
a41c76
-                               _("disk '%s' not found in checkpoint '%s'"),
a41c76
-                               diskdst, incremental[incridx]->name);
a41c76
-                return NULL;
a41c76
-            }
a41c76
-
a41c76
-            continue;
a41c76
-        }
a41c76
-
a41c76
-        if (qemuBackupGetBitmapMergeRange(n, incremental[incridx]->name,
a41c76
-                                          &tmp, &tmpsrc, diskdst,
a41c76
-                                          blockNamedNodeData) < 0)
a41c76
-            return NULL;
a41c76
-
a41c76
-        if (virJSONValueArrayConcat(ret, tmp) < 0)
a41c76
-            return NULL;
a41c76
-
a41c76
-        n = tmpsrc;
a41c76
-    }
a41c76
-
a41c76
-    return g_steal_pointer(&ret;;
a41c76
-}
a41c76
-
a41c76
-
a41c76
 static int
a41c76
 qemuBackupDiskPrepareOneBitmaps(struct qemuBackupDiskData *dd,
a41c76
                                 virJSONValuePtr actions,
a41c76
-                                virDomainMomentDefPtr *incremental,
a41c76
                                 virHashTablePtr blockNamedNodeData)
a41c76
 {
a41c76
-    g_autoptr(virJSONValue) mergebitmaps = NULL;
a41c76
-    g_autoptr(virJSONValue) mergebitmapsstore = NULL;
a41c76
-
a41c76
-    if (!(mergebitmaps = qemuBackupDiskPrepareOneBitmapsChain(incremental,
a41c76
-                                                              dd->domdisk->src,
a41c76
-                                                              blockNamedNodeData,
a41c76
-                                                              dd->domdisk->dst)))
a41c76
-        return -1;
a41c76
-
a41c76
-    if (!(mergebitmapsstore = virJSONValueCopy(mergebitmaps)))
a41c76
-        return -1;
a41c76
-
a41c76
-    if (qemuMonitorTransactionBitmapAdd(actions,
a41c76
-                                        dd->domdisk->src->nodeformat,
a41c76
-                                        dd->incrementalBitmap,
a41c76
-                                        false,
a41c76
-                                        true, 0) < 0)
a41c76
-        return -1;
a41c76
-
a41c76
-    if (qemuMonitorTransactionBitmapMerge(actions,
a41c76
-                                          dd->domdisk->src->nodeformat,
a41c76
-                                          dd->incrementalBitmap,
a41c76
-                                          &mergebitmaps) < 0)
a41c76
+    if (!qemuBlockBitmapChainIsValid(dd->domdisk->src,
a41c76
+                                     dd->backupdisk->incremental,
a41c76
+                                     blockNamedNodeData)) {
a41c76
+        virReportError(VIR_ERR_INVALID_ARG,
a41c76
+                       _("missing or broken bitmap '%s' for disk '%s'"),
a41c76
+                       dd->backupdisk->incremental, dd->domdisk->dst);
a41c76
         return -1;
a41c76
+    }
a41c76
 
a41c76
-    if (qemuMonitorTransactionBitmapAdd(actions,
a41c76
-                                        dd->store->nodeformat,
a41c76
-                                        dd->incrementalBitmap,
a41c76
-                                        false,
a41c76
-                                        true, 0) < 0)
a41c76
+    if (qemuBackupDiskPrepareOneBitmapsChain(dd->domdisk->src,
a41c76
+                                             dd->domdisk->src,
a41c76
+                                             dd->incrementalBitmap,
a41c76
+                                             dd->backupdisk->incremental,
a41c76
+                                             actions,
a41c76
+                                             blockNamedNodeData) < 0)
a41c76
         return -1;
a41c76
 
a41c76
-    if (qemuMonitorTransactionBitmapMerge(actions,
a41c76
-                                          dd->store->nodeformat,
a41c76
-                                          dd->incrementalBitmap,
a41c76
-                                          &mergebitmapsstore) < 0)
a41c76
+    if (qemuBackupDiskPrepareOneBitmapsChain(dd->domdisk->src,
a41c76
+                                             dd->store,
a41c76
+                                             dd->incrementalBitmap,
a41c76
+                                             dd->backupdisk->incremental,
a41c76
+                                             actions,
a41c76
+                                             blockNamedNodeData) < 0)
a41c76
         return -1;
a41c76
 
a41c76
     return 0;
a41c76
@@ -382,7 +241,6 @@ qemuBackupDiskPrepareDataOne(virDomainObjPtr vm,
a41c76
                              virQEMUDriverConfigPtr cfg)
a41c76
 {
a41c76
     qemuDomainObjPrivatePtr priv = vm->privateData;
a41c76
-    g_autofree virDomainMomentDefPtr *incremental = NULL;
a41c76
 
a41c76
     /* set data structure */
a41c76
     dd->backupdisk = backupdisk;
a41c76
@@ -418,16 +276,12 @@ qemuBackupDiskPrepareDataOne(virDomainObjPtr vm,
a41c76
         return -1;
a41c76
 
a41c76
     if (dd->backupdisk->incremental) {
a41c76
-        if (!(incremental = qemuBackupBeginCollectIncrementalCheckpoints(vm, dd->backupdisk->incremental)))
a41c76
-            return -1;
a41c76
-
a41c76
         if (dd->backupdisk->exportbitmap)
a41c76
             dd->incrementalBitmap = g_strdup(dd->backupdisk->exportbitmap);
a41c76
         else
a41c76
             dd->incrementalBitmap = g_strdup_printf("backup-%s", dd->domdisk->dst);
a41c76
 
a41c76
-        if (qemuBackupDiskPrepareOneBitmaps(dd, actions, incremental,
a41c76
-                                            blockNamedNodeData) < 0)
a41c76
+        if (qemuBackupDiskPrepareOneBitmaps(dd, actions, blockNamedNodeData) < 0)
a41c76
             return -1;
a41c76
     }
a41c76
 
a41c76
@@ -881,8 +735,8 @@ qemuBackupBegin(virDomainObjPtr vm,
a41c76
     if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_BACKUP)))
a41c76
         goto endjob;
a41c76
 
a41c76
-    if ((ndd = qemuBackupDiskPrepareData(vm, def, blockNamedNodeData,
a41c76
-                                         actions, cfg, &dd)) <= 0) {
a41c76
+    if ((ndd = qemuBackupDiskPrepareData(vm, def, blockNamedNodeData, actions,
a41c76
+                                         cfg, &dd)) <= 0) {
a41c76
         if (ndd == 0) {
a41c76
             virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
a41c76
                            _("no disks selected for backup"));
a41c76
diff --git a/src/qemu/qemu_backup.h b/src/qemu/qemu_backup.h
a41c76
index 3321ba0b6f..c821baf0ee 100644
a41c76
--- a/src/qemu/qemu_backup.h
a41c76
+++ b/src/qemu/qemu_backup.h
a41c76
@@ -52,8 +52,10 @@ qemuBackupGetJobInfoStats(virQEMUDriverPtr driver,
a41c76
                           qemuDomainJobInfoPtr jobInfo);
a41c76
 
a41c76
 /* exported for testing */
a41c76
-virJSONValuePtr
a41c76
-qemuBackupDiskPrepareOneBitmapsChain(virDomainMomentDefPtr *incremental,
a41c76
-                                     virStorageSourcePtr backingChain,
a41c76
-                                     virHashTablePtr blockNamedNodeData,
a41c76
-                                     const char *diskdst);
a41c76
+int
a41c76
+qemuBackupDiskPrepareOneBitmapsChain(virStorageSourcePtr backingChain,
a41c76
+                                     virStorageSourcePtr targetsrc,
a41c76
+                                     const char *targetbitmap,
a41c76
+                                     const char *incremental,
a41c76
+                                     virJSONValuePtr actions,
a41c76
+                                     virHashTablePtr blockNamedNodeData);
a41c76
diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c
a41c76
index 7c8f67dd6b..dcdc9eade0 100644
a41c76
--- a/tests/qemublocktest.c
a41c76
+++ b/tests/qemublocktest.c
a41c76
@@ -716,65 +716,6 @@ testQemuBitmapGetFakeChainEntry(virStorageSourcePtr src,
a41c76
 }
a41c76
 
a41c76
 
a41c76
-typedef virDomainMomentDefPtr testMomentList;
a41c76
-
a41c76
-static void
a41c76
-testMomentListFree(testMomentList *list)
a41c76
-{
a41c76
-    testMomentList *tmp = list;
a41c76
-
a41c76
-    if (!list)
a41c76
-        return;
a41c76
-
a41c76
-    while (*tmp) {
a41c76
-        virObjectUnref(*tmp);
a41c76
-        tmp++;
a41c76
-    }
a41c76
-
a41c76
-    g_free(list);
a41c76
-}
a41c76
-
a41c76
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(testMomentList, testMomentListFree);
a41c76
-
a41c76
-static virDomainMomentDefPtr
a41c76
-testQemuBackupGetIncrementalMoment(const char *name)
a41c76
-{
a41c76
-    virDomainCheckpointDefPtr checkpoint = NULL;
a41c76
-
a41c76
-    if (!(checkpoint = virDomainCheckpointDefNew()))
a41c76
-        abort();
a41c76
-
a41c76
-    checkpoint->disks = g_new0(virDomainCheckpointDiskDef, 1);
a41c76
-    checkpoint->ndisks = 1;
a41c76
-
a41c76
-    checkpoint->disks[0].name = g_strdup("testdisk");
a41c76
-    checkpoint->disks[0].type = VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP;
a41c76
-
a41c76
-    checkpoint->parent.name = g_strdup(name);
a41c76
-
a41c76
-    return (virDomainMomentDefPtr) checkpoint;
a41c76
-}
a41c76
-
a41c76
-
a41c76
-static virDomainMomentDefPtr *
a41c76
-testQemuBackupGetIncremental(const char *incFrom)
a41c76
-{
a41c76
-    const char *checkpoints[] = {"current", "d", "c", "b", "a"};
a41c76
-    virDomainMomentDefPtr *incr;
a41c76
-    size_t i;
a41c76
-
a41c76
-    incr = g_new0(virDomainMomentDefPtr, G_N_ELEMENTS(checkpoints) + 1);
a41c76
-
a41c76
-    for (i = 0; i < G_N_ELEMENTS(checkpoints); i++) {
a41c76
-        incr[i] = testQemuBackupGetIncrementalMoment(checkpoints[i]);
a41c76
-
a41c76
-        if (STREQ(incFrom, checkpoints[i]))
a41c76
-            break;
a41c76
-    }
a41c76
-
a41c76
-    return incr;
a41c76
-}
a41c76
-
a41c76
 static const char *backupDataPrefix = "qemublocktestdata/backupmerge/";
a41c76
 
a41c76
 struct testQemuBackupIncrementalBitmapCalculateData {
a41c76
@@ -791,10 +732,10 @@ testQemuBackupIncrementalBitmapCalculate(const void *opaque)
a41c76
     const struct testQemuBackupIncrementalBitmapCalculateData *data = opaque;
a41c76
     g_autoptr(virJSONValue) nodedatajson = NULL;
a41c76
     g_autoptr(virHashTable) nodedata = NULL;
a41c76
-    g_autoptr(virJSONValue) mergebitmaps = NULL;
a41c76
-    g_autofree char *actual = NULL;
a41c76
+    g_autoptr(virJSONValue) actions = virJSONValueNewArray();
a41c76
     g_autofree char *expectpath = NULL;
a41c76
-    g_autoptr(testMomentList) incremental = NULL;
a41c76
+    g_autoptr(virStorageSource) target = NULL;
a41c76
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
a41c76
 
a41c76
     expectpath = g_strdup_printf("%s/%s%s-out.json", abs_srcdir,
a41c76
                                  backupDataPrefix, data->name);
a41c76
@@ -808,19 +749,24 @@ testQemuBackupIncrementalBitmapCalculate(const void *opaque)
a41c76
         return -1;
a41c76
     }
a41c76
 
a41c76
-    incremental = testQemuBackupGetIncremental(data->incremental);
a41c76
+    if (!(target = virStorageSourceNew()))
a41c76
+        return -1;
a41c76
 
a41c76
-    if ((mergebitmaps = qemuBackupDiskPrepareOneBitmapsChain(incremental,
a41c76
-                                                             data->chain,
a41c76
-                                                             nodedata,
a41c76
-                                                             "testdisk"))) {
a41c76
-        if (!(actual = virJSONValueToString(mergebitmaps, true)))
a41c76
+    target->nodeformat = g_strdup_printf("target_node");
a41c76
+
a41c76
+    if (qemuBackupDiskPrepareOneBitmapsChain(data->chain,
a41c76
+                                             target,
a41c76
+                                             "target-bitmap-name",
a41c76
+                                             data->incremental,
a41c76
+                                             actions,
a41c76
+                                             nodedata) >= 0) {
a41c76
+        if (virJSONValueToBuffer(actions, &buf, true) < 0)
a41c76
             return -1;
a41c76
     } else {
a41c76
-        actual = g_strdup("NULL\n");
a41c76
+        virBufferAddLit(&buf, "NULL\n");
a41c76
     }
a41c76
 
a41c76
-    return virTestCompareToFile(actual, expectpath);
a41c76
+    return virTestCompareToFile(virBufferCurrentContent(&buf), expectpath);
a41c76
 }
a41c76
 
a41c76
 
a41c76
diff --git a/tests/qemublocktestdata/backupmerge/empty-out.json b/tests/qemublocktestdata/backupmerge/empty-out.json
a41c76
index 7951defec1..41b42e677b 100644
a41c76
--- a/tests/qemublocktestdata/backupmerge/empty-out.json
a41c76
+++ b/tests/qemublocktestdata/backupmerge/empty-out.json
a41c76
@@ -1 +1,3 @@
a41c76
-NULL
a41c76
+[
a41c76
+
a41c76
+]
a41c76
-- 
a41c76
2.27.0
a41c76