|
|
fbe740 |
From 629e54cc030235909720da73d6367fc0b703d062 Mon Sep 17 00:00:00 2001
|
|
|
fbe740 |
Message-Id: <629e54cc030235909720da73d6367fc0b703d062@dist-git>
|
|
|
fbe740 |
From: Peter Krempa <pkrempa@redhat.com>
|
|
|
fbe740 |
Date: Tue, 23 Jun 2020 12:23:56 +0200
|
|
|
fbe740 |
Subject: [PATCH] qemu: block: Add universal helper for merging dirty bitmaps
|
|
|
fbe740 |
for all scenarios
|
|
|
fbe740 |
MIME-Version: 1.0
|
|
|
fbe740 |
Content-Type: text/plain; charset=UTF-8
|
|
|
fbe740 |
Content-Transfer-Encoding: 8bit
|
|
|
fbe740 |
|
|
|
fbe740 |
Add a function which allows merging bitmaps according to the new
|
|
|
fbe740 |
semantics and will allow replacing all the specific ad-hoc functions
|
|
|
fbe740 |
currently in use for 'backup', 'block commit', 'block copy' and will
|
|
|
fbe740 |
also be usable in the future for 'block pull' and non-shared storage
|
|
|
fbe740 |
migration.
|
|
|
fbe740 |
|
|
|
fbe740 |
The semantics are a bit quirky for the 'backup' case but these quirks
|
|
|
fbe740 |
are documented and will prevent us from having two slightly different
|
|
|
fbe740 |
algorithms.
|
|
|
fbe740 |
|
|
|
fbe740 |
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
|
|
|
fbe740 |
Reviewed-by: Eric Blake <eblake@redhat.com>
|
|
|
fbe740 |
(cherry picked from commit 4fa8654eced8b0362d3f3ff33eebb108fe833869)
|
|
|
fbe740 |
https://bugzilla.redhat.com/show_bug.cgi?id=1804593
|
|
|
fbe740 |
Message-Id: <25acdc313844a20a9c884048498c42b9a8105de7.1592906423.git.pkrempa@redhat.com>
|
|
|
fbe740 |
Reviewed-by: Ján Tomko <jtomko@redhat.com>
|
|
|
fbe740 |
---
|
|
|
fbe740 |
src/qemu/qemu_block.c | 172 ++++++++++++++++++++++++++++++++++++++++++
|
|
|
fbe740 |
src/qemu/qemu_block.h | 10 +++
|
|
|
fbe740 |
2 files changed, 182 insertions(+)
|
|
|
fbe740 |
|
|
|
fbe740 |
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
|
|
|
fbe740 |
index 38c8269721..b2296c2b4c 100644
|
|
|
fbe740 |
--- a/src/qemu/qemu_block.c
|
|
|
fbe740 |
+++ b/src/qemu/qemu_block.c
|
|
|
fbe740 |
@@ -2832,6 +2832,178 @@ qemuBlockGetNamedNodeData(virDomainObjPtr vm,
|
|
|
fbe740 |
}
|
|
|
fbe740 |
|
|
|
fbe740 |
|
|
|
fbe740 |
+/**
|
|
|
fbe740 |
+ * qemuBlockGetBitmapMergeActionsGetBitmaps:
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * Collect a list of bitmaps which need to be handled in
|
|
|
fbe740 |
+ * qemuBlockGetBitmapMergeActions. The list contains only valid bitmaps in the
|
|
|
fbe740 |
+ * sub-chain which is being processed.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * Note that the returned GSList contains bitmap names string pointers borrowed
|
|
|
fbe740 |
+ * from @blockNamedNodeData so they must not be freed.
|
|
|
fbe740 |
+ */
|
|
|
fbe740 |
+static GSList *
|
|
|
fbe740 |
+qemuBlockGetBitmapMergeActionsGetBitmaps(virStorageSourcePtr topsrc,
|
|
|
fbe740 |
+ const char *bitmapname,
|
|
|
fbe740 |
+ virHashTablePtr blockNamedNodeData)
|
|
|
fbe740 |
+{
|
|
|
fbe740 |
+ g_autoptr(GSList) ret = NULL;
|
|
|
fbe740 |
+ qemuBlockNamedNodeDataPtr entry;
|
|
|
fbe740 |
+ size_t i;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ /* for now it doesn't make sense to consider bitmaps which are not present
|
|
|
fbe740 |
+ * in @topsrc as we can't recreate a bitmap for a layer if it's missing */
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (!(entry = virHashLookup(blockNamedNodeData, topsrc->nodeformat)))
|
|
|
fbe740 |
+ return NULL;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ for (i = 0; i < entry->nbitmaps; i++) {
|
|
|
fbe740 |
+ qemuBlockNamedNodeDataBitmapPtr bitmap = entry->bitmaps[i];
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (bitmapname &&
|
|
|
fbe740 |
+ STRNEQ(bitmapname, bitmap->name))
|
|
|
fbe740 |
+ continue;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (!qemuBlockBitmapChainIsValid(topsrc, bitmap->name, blockNamedNodeData))
|
|
|
fbe740 |
+ continue;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ ret = g_slist_prepend(ret, bitmap->name);
|
|
|
fbe740 |
+ }
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ return g_steal_pointer(&ret;;
|
|
|
fbe740 |
+}
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+/**
|
|
|
fbe740 |
+ * qemuBlockGetBitmapMergeActions:
|
|
|
fbe740 |
+ * @topsrc: top of the chain to merge bitmaps in
|
|
|
fbe740 |
+ * @basesrc: bottom of the chain to merge bitmaps in (NULL for full chain)
|
|
|
fbe740 |
+ * @target: destination storage source of the merge (may be part of original chain)
|
|
|
fbe740 |
+ * @bitmapname: name of bitmap to perform the merge (NULL for all bitmaps)
|
|
|
fbe740 |
+ * @dstbitmapname: name of destination bitmap of the merge (see below for caveats)
|
|
|
fbe740 |
+ * @writebitmapsrc: storage source corresponding to the node containing the write temporary bitmap
|
|
|
fbe740 |
+ * @actions: returns actions for a 'transaction' QMP command for executing the merge
|
|
|
fbe740 |
+ * @blockNamedNodeData: hash table filled with qemuBlockNamedNodeData
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * Calculate handling of dirty block bitmaps between @topsrc and @basesrc. If
|
|
|
fbe740 |
+ * @basesrc is NULL the end of the chain is considered. @target is the destination
|
|
|
fbe740 |
+ * storage source definition of the merge and may or may not be part of the
|
|
|
fbe740 |
+ * merged chain.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * Specifically the merging algorithm ensures that each considered bitmap is
|
|
|
fbe740 |
+ * merged with the appropriate bitmaps so that it properly describes
|
|
|
fbe740 |
+ * the state of dirty blocks when looked at from @topsrc based on the depth
|
|
|
fbe740 |
+ * of the backing chain where the bitmap is placed.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * If @bitmapname is non-NULL only bitmaps with that name are handled, otherwise
|
|
|
fbe740 |
+ * all bitmaps are considered.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * If @dstbitmap is non-NULL everything is merged into a bitmap with that name,
|
|
|
fbe740 |
+ * otherwise each bitmap is merged into a bitmap with the same name into @target.
|
|
|
fbe740 |
+ * Additionally if @dstbitmap is non-NULL the target bitmap is created as 'inactive'
|
|
|
fbe740 |
+ * and 'transient' as a special case for the backup operation.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * If @writebitmapsrc is non-NULL, the 'libvirt-tmp-activewrite' bitmap from
|
|
|
fbe740 |
+ * given node is merged along with others. This bitmap corresponds to the writes
|
|
|
fbe740 |
+ * which occurred between an active layer job finished and the rest of the bitmap
|
|
|
fbe740 |
+ * merging.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * If the bitmap is not valid somehow (see qemuBlockBitmapChainIsValid) given
|
|
|
fbe740 |
+ * bitmap is silently skipped, so callers must ensure that given bitmap is valid
|
|
|
fbe740 |
+ * if they care about it.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * The resulting 'transaction' QMP command actions are filled in and returned via
|
|
|
fbe740 |
+ * @actions.
|
|
|
fbe740 |
+ *
|
|
|
fbe740 |
+ * Note that @actions may be NULL if no merging is required.
|
|
|
fbe740 |
+ */
|
|
|
fbe740 |
+int
|
|
|
fbe740 |
+qemuBlockGetBitmapMergeActions(virStorageSourcePtr topsrc,
|
|
|
fbe740 |
+ virStorageSourcePtr basesrc,
|
|
|
fbe740 |
+ virStorageSourcePtr target,
|
|
|
fbe740 |
+ const char *bitmapname,
|
|
|
fbe740 |
+ const char *dstbitmapname,
|
|
|
fbe740 |
+ virStorageSourcePtr writebitmapsrc,
|
|
|
fbe740 |
+ virJSONValuePtr *actions,
|
|
|
fbe740 |
+ virHashTablePtr blockNamedNodeData)
|
|
|
fbe740 |
+{
|
|
|
fbe740 |
+ g_autoptr(virJSONValue) act = virJSONValueNewArray();
|
|
|
fbe740 |
+ virStorageSourcePtr n;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ g_autoptr(GSList) bitmaps = NULL;
|
|
|
fbe740 |
+ GSList *next;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (!(bitmaps = qemuBlockGetBitmapMergeActionsGetBitmaps(topsrc, bitmapname,
|
|
|
fbe740 |
+ blockNamedNodeData)))
|
|
|
fbe740 |
+ return 0;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ for (next = bitmaps; next; next = next->next) {
|
|
|
fbe740 |
+ const char *curbitmap = next->data;
|
|
|
fbe740 |
+ const char *mergebitmapname = dstbitmapname;
|
|
|
fbe740 |
+ bool mergebitmappersistent = false;
|
|
|
fbe740 |
+ bool mergebitmapdisabled = true;
|
|
|
fbe740 |
+ g_autoptr(virJSONValue) merge = virJSONValueNewArray();
|
|
|
fbe740 |
+ unsigned long long granularity = 0;
|
|
|
fbe740 |
+ qemuBlockNamedNodeDataBitmapPtr bitmap;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ /* explicitly named destinations mean that we want a temporary
|
|
|
fbe740 |
+ * disabled bitmap only, so undo the default for non-explicit cases */
|
|
|
fbe740 |
+ if (!mergebitmapname) {
|
|
|
fbe740 |
+ mergebitmapname = curbitmap;
|
|
|
fbe740 |
+ mergebitmappersistent = true;
|
|
|
fbe740 |
+ mergebitmapdisabled = false;
|
|
|
fbe740 |
+ }
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ for (n = topsrc; virStorageSourceIsBacking(n) && n != basesrc; n = n->backingStore) {
|
|
|
fbe740 |
+ if (!(bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
fbe740 |
+ n, curbitmap)))
|
|
|
fbe740 |
+ continue;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (granularity == 0)
|
|
|
fbe740 |
+ granularity = bitmap->granularity;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(merge,
|
|
|
fbe740 |
+ n->nodeformat,
|
|
|
fbe740 |
+ bitmap->name) < 0)
|
|
|
fbe740 |
+ return -1;
|
|
|
fbe740 |
+ }
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (dstbitmapname ||
|
|
|
fbe740 |
+ !(bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
|
fbe740 |
+ target, curbitmap))) {
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (qemuMonitorTransactionBitmapAdd(act,
|
|
|
fbe740 |
+ target->nodeformat,
|
|
|
fbe740 |
+ mergebitmapname,
|
|
|
fbe740 |
+ mergebitmappersistent,
|
|
|
fbe740 |
+ mergebitmapdisabled,
|
|
|
fbe740 |
+ granularity) < 0)
|
|
|
fbe740 |
+ return -1;
|
|
|
fbe740 |
+ }
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (writebitmapsrc &&
|
|
|
fbe740 |
+ qemuMonitorTransactionBitmapMergeSourceAddBitmap(merge,
|
|
|
fbe740 |
+ writebitmapsrc->nodeformat,
|
|
|
fbe740 |
+ "libvirt-tmp-activewrite") < 0)
|
|
|
fbe740 |
+ return -1;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (qemuMonitorTransactionBitmapMerge(act, target->nodeformat,
|
|
|
fbe740 |
+ mergebitmapname, &merge) < 0)
|
|
|
fbe740 |
+ return -1;
|
|
|
fbe740 |
+ }
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (writebitmapsrc &&
|
|
|
fbe740 |
+ qemuMonitorTransactionBitmapRemove(act, writebitmapsrc->nodeformat,
|
|
|
fbe740 |
+ "libvirt-tmp-activewrite") < 0)
|
|
|
fbe740 |
+ return -1;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ if (virJSONValueArraySize(act) > 0)
|
|
|
fbe740 |
+ *actions = g_steal_pointer(&act;;
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+ return 0;
|
|
|
fbe740 |
+}
|
|
|
fbe740 |
+
|
|
|
fbe740 |
+
|
|
|
fbe740 |
/**
|
|
|
fbe740 |
* qemuBlockBitmapChainIsValid:
|
|
|
fbe740 |
*
|
|
|
fbe740 |
diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h
|
|
|
fbe740 |
index 06afa54115..2500390734 100644
|
|
|
fbe740 |
--- a/src/qemu/qemu_block.h
|
|
|
fbe740 |
+++ b/src/qemu/qemu_block.h
|
|
|
fbe740 |
@@ -221,6 +221,16 @@ virHashTablePtr
|
|
|
fbe740 |
qemuBlockGetNamedNodeData(virDomainObjPtr vm,
|
|
|
fbe740 |
qemuDomainAsyncJob asyncJob);
|
|
|
fbe740 |
|
|
|
fbe740 |
+int
|
|
|
fbe740 |
+qemuBlockGetBitmapMergeActions(virStorageSourcePtr topsrc,
|
|
|
fbe740 |
+ virStorageSourcePtr basesrc,
|
|
|
fbe740 |
+ virStorageSourcePtr target,
|
|
|
fbe740 |
+ const char *bitmapname,
|
|
|
fbe740 |
+ const char *dstbitmapname,
|
|
|
fbe740 |
+ virStorageSourcePtr writebitmapsrc,
|
|
|
fbe740 |
+ virJSONValuePtr *actions,
|
|
|
fbe740 |
+ virHashTablePtr blockNamedNodeData);
|
|
|
fbe740 |
+
|
|
|
fbe740 |
bool
|
|
|
fbe740 |
qemuBlockBitmapChainIsValid(virStorageSourcePtr src,
|
|
|
fbe740 |
const char *bitmapname,
|
|
|
fbe740 |
--
|
|
|
fbe740 |
2.27.0
|
|
|
fbe740 |
|