On Thu, Feb 11, 2021 at 16:37:57 +0100, Peter Krempa wrote: > Preserve block dirty bitmaps after migration with > QEMU_MONITOR_MIGRATE_NON_SHARED_(DISK|INC). > > This patch implements functions which offer the bitmaps to the > destination, check for eligibility on destination and then configure > source for the migration. > > Signed-off-by: Peter Krempa <pkrempa@xxxxxxxxxx> > --- > src/qemu/qemu_migration.c | 333 +++++++++++++++++++++++++++++++++++++- > 1 file changed, 331 insertions(+), 2 deletions(-) > > diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c > index 36424f8493..16bfad0390 100644 > --- a/src/qemu/qemu_migration.c > +++ b/src/qemu/qemu_migration.c ... > @@ -2528,6 +2619,92 @@ qemuMigrationDstPrepare(virDomainObjPtr vm, > migrateFrom, fd, NULL); > } > > + > +/** > + * qemuMigrationDstPrepareAnyBlockDirtyBitmaps: > + * @vm: domain object > + * @mig: migration cookie > + * @migParams: migration parameters > + * @flags: migration flags > + * > + * Checks whether block dirty bitmaps offered by the migration source are > + * to be migrated (e.g. they don't exist, the destination is compatible etc) > + * and sets up destination qemu for migrating the bitmaps as well as updates the > + * list of eligible bitmaps in the migration cookie to be sent back to the > + * source. > + */ > +static int > +qemuMigrationDstPrepareAnyBlockDirtyBitmaps(virDomainObjPtr vm, > + qemuMigrationCookiePtr mig, > + qemuMigrationParamsPtr migParams, > + unsigned int flags) > +{ > + qemuDomainObjPrivatePtr priv = vm->privateData; > + g_autoptr(virJSONValue) mapping = NULL; > + g_autoptr(GHashTable) blockNamedNodeData = NULL; > + GSList *nextdisk; > + > + if (!mig->nbd || > + !mig->blockDirtyBitmaps || > + !(flags & (VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC)) || > + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING)) > + return 0; Shouldn't we report an error in case the source sent bitmaps, but local QEMU does not support QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING? > + > + if (qemuMigrationCookieBlockDirtyBitmapsMatchDisks(vm->def, mig->blockDirtyBitmaps) < 0) > + return -1; > + > + if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_MIGRATION_IN))) > + return -1; > + > + for (nextdisk = mig->blockDirtyBitmaps; nextdisk; nextdisk = nextdisk->next) { > + qemuMigrationBlockDirtyBitmapsDiskPtr disk = nextdisk->data; > + qemuBlockNamedNodeDataPtr nodedata; > + GSList *nextbitmap; > + > + if (!(nodedata = virHashLookup(blockNamedNodeData, disk->nodename))) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + _("failed to find data for block node '%s'"), > + disk->nodename); > + return -1; > + } > + > + /* don't migrate bitmaps into non-qcow2v3+ images */ How about "Bitmaps can only be migrated to qcow2 v3+"? > + if (disk->disk->src->format != VIR_STORAGE_FILE_QCOW2 || > + nodedata->qcow2v2) { > + disk->skip = true; Is skipping the disk the right thing to do? Should we report an error and abort migration instead? Just checking, maybe we can't do so for backward compatibility... > + continue; > + } > + > + for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) { > + qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap = nextbitmap->data; > + size_t k; > + > + /* don't migrate into existing bitmaps */ > + for (k = 0; k < nodedata->nbitmaps; k++) { > + if (STREQ(bitmap->bitmapname, nodedata->bitmaps[k]->name)) { > + bitmap->skip = true; And similar questions for bitmaps here. > + break; > + } > + } > + > + if (bitmap->skip) > + continue; > + } > + } > + > + if (qemuMigrationCookieBlockDirtyBitmapsToParams(mig->blockDirtyBitmaps, > + &mapping) < 0) > + return -1; > + > + if (!mapping) > + return 0; > + > + qemuMigrationParamsSetBlockDirtyBitmapMapping(migParams, &mapping); > + mig->flags |= QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; > + return 0; > +} > + > + > static int > qemuMigrationDstPrepareAny(virQEMUDriverPtr driver, > virConnectPtr dconn, ... > +static int > +qemuMigrationSrcRunPrepareBlockDirtyBitmaps(virDomainObjPtr vm, > + qemuMigrationCookiePtr mig, > + qemuMigrationParamsPtr migParams, > + unsigned int flags) > + > +{ > + g_autoptr(virJSONValue) mapping = NULL; > + > + if (!mig->blockDirtyBitmaps) > + return 0; > + > + if (qemuMigrationCookieBlockDirtyBitmapsMatchDisks(vm->def, mig->blockDirtyBitmaps) < 0) > + return -1; > + > + /* For QEMU_MONITOR_MIGRATE_NON_SHARED_INC we can migrate the bitmaps > + * directly, otherwise we must create merged bitmaps from the whole > + * chain */ > + > + if (!(flags & QEMU_MONITOR_MIGRATE_NON_SHARED_INC) && > + qemuMigrationSrcRunPrepareBlockDirtyBitmapsMerge(vm, mig)) "< 0" is missing in the check. > + return -1; > + > + if (qemuMigrationCookieBlockDirtyBitmapsToParams(mig->blockDirtyBitmaps, > + &mapping) < 0) > + return -1; > + > + qemuMigrationParamsSetBlockDirtyBitmapMapping(migParams, &mapping); > + return 0; > +} > + > + > static int > qemuMigrationSrcRun(virQEMUDriverPtr driver, > virDomainObjPtr vm, Jirka