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