qemu: block: Add universal helper for merging dirty bitmaps for all scenarios

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 <pkrempa@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Peter Krempa 2020-06-01 12:05:49 +02:00
parent 2c5a3da365
commit 4fa8654ece
2 changed files with 182 additions and 0 deletions

View File

@ -2847,6 +2847,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:
*

View File

@ -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,