qemuBlockBitmapTemporaryAdd uses the 'block-dirty-bitmap-populate' qemu blockjob to fill a new bitmap from the allocation layer. This is useful to restore bitmaps for backing chain layers where a specific bitmap is not necessary or the layer was created outside of libvirt. Signed-off-by: Peter Krempa <pkrempa@xxxxxxxxxx> --- src/qemu/qemu_block.c | 190 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 11 +++ src/qemu/qemu_blockjob.c | 2 +- 3 files changed, 202 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index f42fd200a3..69578e861c 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -3434,3 +3434,193 @@ qemuBlockUpdateRelativeBacking(virDomainObjPtr vm, return 0; } + + +static void +qemuBlockBitmapTemporaryRemoveDuplicates(GSList *images) +{ + g_autoptr(virHashTable) duplicates = virHashNew(NULL); + GSList *next; + GSList *prev = NULL; + + for (next = images; next; next = next->next) { + virStorageSourcePtr img = next->data; + + if (virHashHasEntry(duplicates, img->nodeformat)) { + next = g_slist_delete_link(prev, next); + continue; + } + + virHashAddEntry(duplicates, img->nodeformat, NULL); + + prev = next; + } +} + + +/** + * qemuBlockBitmapTemporaryAdd: + * @vm: domain object + * @blockNamedNodeData: hash table filled with qemuBlockNamedNodeData + * @images: a GSList of virStorageSources to add the temporary bitmaps for + * @asyncJob: qemu asynchronous job type + * + * Add temporary block dirty bitmaps populated from the allocation map of + * images for list of virStorageSources @images. The bitmaps added are called + * "libvirt-tmp-allocation" and are not made persistent. After this function + * returns @images is updated to the actual list of bitmaps which were added and + * qemuBlockBitmapTemporaryRemove can be used to undo the changes. + */ +int +qemuBlockBitmapTemporaryAdd(virDomainObjPtr vm, + virHashTablePtr blockNamedNodeData, + GSList **images, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + g_autoptr(virJSONValue) actions = virJSONValueNewArray(); + bool failed = false; + GSList *next; + int rc; + + if (!*images) + return 0; + + qemuBlockBitmapTemporaryRemoveDuplicates(*images); + + for (next = *images; next; next = next->next) { + virStorageSourcePtr img = next->data; + qemuBlockNamedNodeDataBitmapPtr tmpbitmap; + qemuDomainStorageSourcePrivatePtr srcPriv; + + if ((tmpbitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData, img, + "libvirt-tmp-allocation"))) { + if (!tmpbitmap->recording || tmpbitmap->persistent || tmpbitmap->inconsistent) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("internal bitmap in unexpected state")); + return -1; + } + } else { + if (qemuMonitorTransactionBitmapAdd(actions, + img->nodeformat, + "libvirt-tmp-allocation", + false, false, 0) < 0) + return -1; + } + + if (!(srcPriv = qemuDomainStorageSourcePrivateFetch(img))) + return -1; + + if (!(srcPriv->blockjob = qemuBlockJobNewPopulate(vm, img))) + return -1; + + qemuBlockJobSyncBegin(srcPriv->blockjob); + + if (qemuMonitorTransactionBitmapPopulate(actions, + img->nodeformat, + "libvirt-tmp-allocation", + srcPriv->blockjob->name) < 0) + return -1; + } + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + return -1; + + rc = qemuMonitorTransaction(priv->mon, &actions); + + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0) + return -1; + + while (true) { + bool update = false; + bool running = false; + + for (next = *images; next; next = next->next) { + virStorageSourcePtr img = next->data; + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(img); + + if (!srcPriv->blockjob) + continue; + + if (srcPriv->blockjob->newstate != -1) + update = true; + + qemuBlockJobUpdate(vm, srcPriv->blockjob, asyncJob); + + switch ((qemuBlockjobState) srcPriv->blockjob->state) { + case QEMU_BLOCKJOB_STATE_NEW: + case QEMU_BLOCKJOB_STATE_RUNNING: + running = true; + break; + + case QEMU_BLOCKJOB_STATE_FAILED: + case QEMU_BLOCKJOB_STATE_CANCELLED: + failed = true; + G_GNUC_FALLTHROUGH; + /* completed is assumed once no job has failed and no job is running */ + case QEMU_BLOCKJOB_STATE_COMPLETED: + virObjectUnref(srcPriv->blockjob); + srcPriv->blockjob = NULL; + break; + + /* other states are impossible in this case */ + case QEMU_BLOCKJOB_STATE_READY: + case QEMU_BLOCKJOB_STATE_CONCLUDED: + case QEMU_BLOCKJOB_STATE_ABORTING: + case QEMU_BLOCKJOB_STATE_PIVOTING: + case QEMU_BLOCKJOB_STATE_LAST: + break; + } + } + + /* Updating job will cause monitor access which in turn allows other + * events to be processed. We must ensure to re-process the list before + * waiting to prevent getting stuck */ + if (update) + continue; + + if (!running) + break; + + if (virDomainObjWait(vm) < 0) + return -1; + } + + if (failed) { + qemuBlockBitmapTemporaryRemove(vm, *images, asyncJob); + g_slist_free(*images); + *images = NULL; + return -1; + } + + return 0; +} + + +void +qemuBlockBitmapTemporaryRemove(virDomainObjPtr vm, + GSList *images, + qemuDomainAsyncJob asyncJob) + +{ + VIR_ERROR_AUTOPRESERVE_LAST; + qemuDomainObjPrivatePtr priv = vm->privateData; + g_autoptr(virJSONValue) actions = virJSONValueNewArray(); + + if (!images) + return; + + for (; images; images = images->next) { + virStorageSourcePtr img = images->data; + + if (qemuMonitorTransactionBitmapRemove(actions, img->nodeformat, "libvirt-tmp-allocation") < 0) + return; + } + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + return; + + qemuMonitorTransaction(priv->mon, &actions); + + ignore_value(qemuDomainObjExitMonitor(priv->driver, vm)); +} diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index 2ad2ce1a1f..ccd6f57440 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -266,3 +266,14 @@ int qemuBlockUpdateRelativeBacking(virDomainObjPtr vm, virStorageSourcePtr src, virStorageSourcePtr topsrc); + +int +qemuBlockBitmapTemporaryAdd(virDomainObjPtr vm, + virHashTablePtr blockNamedNodeData, + GSList **images, + qemuDomainAsyncJob asyncJob); + +void +qemuBlockBitmapTemporaryRemove(virDomainObjPtr vm, + GSList *images, + qemuDomainAsyncJob asyncJob); diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 79820c6ca8..b19d96b312 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -1528,7 +1528,7 @@ qemuBlockJobProcessEventConcludedPopulate(virQEMUDriverPtr driver, if (qemuMonitorTransactionBitmapRemove(actions, job->data.populate.src->nodeformat, - "libvirt-tmp-bitmap") < 0) + "libvirt-tmp-allocation") < 0) return; if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) -- 2.26.2