[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v2 1/3] migration: Add block-bitmap-mapping parameter
From: |
Dr. David Alan Gilbert |
Subject: |
Re: [PATCH v2 1/3] migration: Add block-bitmap-mapping parameter |
Date: |
Wed, 22 Jul 2020 10:06:53 +0100 |
User-agent: |
Mutt/1.14.5 (2020-06-23) |
* Max Reitz (mreitz@redhat.com) wrote:
> This migration parameter allows mapping block node names and bitmap
> names to aliases for the purpose of block dirty bitmap migration.
>
> This way, management tools can use different node and bitmap names on
> the source and destination and pass the mapping of how bitmaps are to be
> transferred to qemu (on the source, the destination, or even both with
> arbitrary aliases in the migration stream).
>
> Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> Signed-off-by: Max Reitz <mreitz@redhat.com>
For both HMP and migration stuff:
Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> Vladimir noted in v1 that it would be better to ignore bitmaps whose
> names aren't mapped, or that are on nodes whose names aren't mapped.
> One of the reasons he gave was that bitmap migration is inherently a
> form of postcopy migration, and we should not break the target when it
> is already running because of a bitmap issue.
>
> So in this version, I changed the behavior to just ignore bitmaps
> without a mapping on the source side. On the destination, however, I
> kept it an error when an incoming bitmap or node alias is unknown.
>
> This is in violation of Vladimir's (rightful) reasoning that such
> errors should never break the already running target, but I decided to
> do so anyway for a couple of reasons:
>
> - Ignoring unmapped bitmaps on the source is trivial: We just have to
> not put them into the migration stream.
> On the destination, it isn't so easy: We (I think) would have to
> modify the code to actively skip the bitmap in the stream.
> (On the other hand, erroring out is always easy.)
>
> - Incoming bitmaps with unknown names are already an error before this
> series. So this isn't introducing new breakage.
>
> - I think it makes sense to drop all bitmaps you don't want to migrate
> (or implicitly drop them by just not specifying them if you don't care
> about them) on the source. I can't imagine a scenario where it would
> be really useful if the destination could silently drop unknown
> bitmaps. Unknown bitmaps should just be dropped on the source.
>
> - Choosing to make it an error now doesn't prevent us from relaxing that
> restriction in the future.
> ---
> qapi/migration.json | 92 +++++++-
> migration/migration.h | 3 +
> migration/block-dirty-bitmap.c | 373 ++++++++++++++++++++++++++++-----
> migration/migration.c | 30 +++
> monitor/hmp-cmds.c | 30 +++
> 5 files changed, 473 insertions(+), 55 deletions(-)
>
> diff --git a/qapi/migration.json b/qapi/migration.json
> index d5000558c6..1b0fbcef96 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -507,6 +507,44 @@
> 'data': [ 'none', 'zlib',
> { 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] }
>
> +##
> +# @BitmapMigrationBitmapAlias:
> +#
> +# @name: The name of the bitmap.
> +#
> +# @alias: An alias name for migration (for example the bitmap name on
> +# the opposite site).
> +#
> +# Since: 5.1
> +##
> +{ 'struct': 'BitmapMigrationBitmapAlias',
> + 'data': {
> + 'name': 'str',
> + 'alias': 'str'
> + } }
> +
> +##
> +# @BitmapMigrationNodeAlias:
> +#
> +# Maps a block node name and the bitmaps it has to aliases for dirty
> +# bitmap migration.
> +#
> +# @node-name: A block node name.
> +#
> +# @alias: An alias block node name for migration (for example the
> +# node name on the opposite site).
> +#
> +# @bitmaps: Mappings for the bitmaps on this node.
> +#
> +# Since: 5.1
> +##
> +{ 'struct': 'BitmapMigrationNodeAlias',
> + 'data': {
> + 'node-name': 'str',
> + 'alias': 'str',
> + 'bitmaps': [ 'BitmapMigrationBitmapAlias' ]
> + } }
> +
> ##
> # @MigrationParameter:
> #
> @@ -641,6 +679,21 @@
> # will consume more CPU.
> # Defaults to 1. (Since 5.0)
> #
> +# @block-bitmap-mapping: Maps block nodes and bitmaps on them to
> +# aliases for the purpose of dirty bitmap migration. Such
> +# aliases may for example be the corresponding names on the
> +# opposite site.
> +# The mapping must be one-to-one, and on the destination also
> +# complete: On the source, bitmaps on nodes where either the
> +# bitmap or the node is not mapped will be ignored. In
> +# contrast, on the destination, receiving a bitmap (by alias)
> +# from a node (by alias) when either alias is not mapped will
> +# result in an error.
> +# By default (when this parameter has never been set), bitmap
> +# names are mapped to themselves. Nodes are mapped to their
> +# block device name if there is one, and to their node name
> +# otherwise. (Since 5.2)
> +#
> # Since: 2.4
> ##
> { 'enum': 'MigrationParameter',
> @@ -655,7 +708,8 @@
> 'multifd-channels',
> 'xbzrle-cache-size', 'max-postcopy-bandwidth',
> 'max-cpu-throttle', 'multifd-compression',
> - 'multifd-zlib-level' ,'multifd-zstd-level' ] }
> + 'multifd-zlib-level' ,'multifd-zstd-level',
> + 'block-bitmap-mapping' ] }
>
> ##
> # @MigrateSetParameters:
> @@ -781,6 +835,21 @@
> # will consume more CPU.
> # Defaults to 1. (Since 5.0)
> #
> +# @block-bitmap-mapping: Maps block nodes and bitmaps on them to
> +# aliases for the purpose of dirty bitmap migration. Such
> +# aliases may for example be the corresponding names on the
> +# opposite site.
> +# The mapping must be one-to-one, and on the destination also
> +# complete: On the source, bitmaps on nodes where either the
> +# bitmap or the node is not mapped will be ignored. In
> +# contrast, on the destination, receiving a bitmap (by alias)
> +# from a node (by alias) when either alias is not mapped will
> +# result in an error.
> +# By default (when this parameter has never been set), bitmap
> +# names are mapped to themselves. Nodes are mapped to their
> +# block device name if there is one, and to their node name
> +# otherwise. (Since 5.2)
> +#
> # Since: 2.4
> ##
> # TODO either fuse back into MigrationParameters, or make
> @@ -811,7 +880,8 @@
> '*max-cpu-throttle': 'int',
> '*multifd-compression': 'MultiFDCompression',
> '*multifd-zlib-level': 'int',
> - '*multifd-zstd-level': 'int' } }
> + '*multifd-zstd-level': 'int',
> + '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ] } }
>
> ##
> # @migrate-set-parameters:
> @@ -957,6 +1027,21 @@
> # will consume more CPU.
> # Defaults to 1. (Since 5.0)
> #
> +# @block-bitmap-mapping: Maps block nodes and bitmaps on them to
> +# aliases for the purpose of dirty bitmap migration. Such
> +# aliases may for example be the corresponding names on the
> +# opposite site.
> +# The mapping must be one-to-one, and on the destination also
> +# complete: On the source, bitmaps on nodes where either the
> +# bitmap or the node is not mapped will be ignored. In
> +# contrast, on the destination, receiving a bitmap (by alias)
> +# from a node (by alias) when either alias is not mapped will
> +# result in an error.
> +# By default (when this parameter has never been set), bitmap
> +# names are mapped to themselves. Nodes are mapped to their
> +# block device name if there is one, and to their node name
> +# otherwise. (Since 5.2)
> +#
> # Since: 2.4
> ##
> { 'struct': 'MigrationParameters',
> @@ -985,7 +1070,8 @@
> '*max-cpu-throttle': 'uint8',
> '*multifd-compression': 'MultiFDCompression',
> '*multifd-zlib-level': 'uint8',
> - '*multifd-zstd-level': 'uint8' } }
> + '*multifd-zstd-level': 'uint8',
> + '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ] } }
>
> ##
> # @query-migrate-parameters:
> diff --git a/migration/migration.h b/migration/migration.h
> index f617960522..038c318b92 100644
> --- a/migration/migration.h
> +++ b/migration/migration.h
> @@ -336,6 +336,9 @@ void migrate_send_rp_resume_ack(MigrationIncomingState
> *mis, uint32_t value);
>
> void dirty_bitmap_mig_before_vm_start(void);
> void init_dirty_bitmap_incoming_migration(void);
> +bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList
> *bbm,
> + Error **errp);
> +
> void migrate_add_address(SocketAddress *address);
>
> int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque);
> diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c
> index b0dbf9eeed..c7945c631b 100644
> --- a/migration/block-dirty-bitmap.c
> +++ b/migration/block-dirty-bitmap.c
> @@ -29,10 +29,10 @@
> *
> * # Header (shared for different chunk types)
> * 1, 2 or 4 bytes: flags (see qemu_{put,put}_flags)
> - * [ 1 byte: node name size ] \ flags & DEVICE_NAME
> - * [ n bytes: node name ] /
> - * [ 1 byte: bitmap name size ] \ flags & BITMAP_NAME
> - * [ n bytes: bitmap name ] /
> + * [ 1 byte: node alias size ] \ flags & DEVICE_NAME
> + * [ n bytes: node alias ] /
> + * [ 1 byte: bitmap alias size ] \ flags & BITMAP_NAME
> + * [ n bytes: bitmap alias ] /
> *
> * # Start of bitmap migration (flags & START)
> * header
> @@ -72,7 +72,9 @@
> #include "migration/register.h"
> #include "qemu/hbitmap.h"
> #include "qemu/cutils.h"
> +#include "qemu/id.h"
> #include "qapi/error.h"
> +#include "qapi/qapi-commands-migration.h"
> #include "trace.h"
>
> #define CHUNK_SIZE (1 << 10)
> @@ -103,7 +105,8 @@
> typedef struct DirtyBitmapMigBitmapState {
> /* Written during setup phase. */
> BlockDriverState *bs;
> - const char *node_name;
> + char *node_alias;
> + char *bitmap_alias;
> BdrvDirtyBitmap *bitmap;
> uint64_t total_sectors;
> uint64_t sectors_per_chunk;
> @@ -128,7 +131,8 @@ typedef struct DirtyBitmapMigState {
>
> typedef struct DirtyBitmapLoadState {
> uint32_t flags;
> - char node_name[256];
> + char node_alias[256];
> + char bitmap_alias[256];
> char bitmap_name[256];
> BlockDriverState *bs;
> BdrvDirtyBitmap *bitmap;
> @@ -144,6 +148,162 @@ typedef struct DirtyBitmapLoadBitmapState {
> static GSList *enabled_bitmaps;
> QemuMutex finish_lock;
>
> +/* For hash tables that map node/bitmap names to aliases */
> +typedef struct AliasMapInnerNode {
> + char *string;
> + GHashTable *subtree;
> +} AliasMapInnerNode;
> +
> +static void free_alias_map_inner_node(void *amin_ptr)
> +{
> + AliasMapInnerNode *amin = amin_ptr;
> +
> + g_free(amin->string);
> + g_hash_table_unref(amin->subtree);
> + g_free(amin);
> +}
> +
> +/**
> + * Construct an alias map based on the given QMP structure.
> + *
> + * (Note that we cannot store such maps in the MigrationParameters
> + * object, because that struct is defined by the QAPI schema, which
> + * makes it basically impossible to have dicts with arbitrary keys.
> + * Therefore, we instead have to construct these maps when migration
> + * starts.)
> + *
> + * @bbm is the block_bitmap_mapping from the migration parameters.
> + *
> + * If @name_to_alias is true, the returned hash table will map node
> + * and bitmap names to their respective aliases (for outgoing
> + * migration).
> + *
> + * If @name_to_alias is false, the returned hash table will map node
> + * and bitmap aliases to their respective names (for incoming
> + * migration).
> + *
> + * The hash table maps node names/aliases to AliasMapInnerNode
> + * objects, whose .string is the respective node alias/name, and whose
> + * .subtree table maps bitmap names/aliases to the respective bitmap
> + * alias/name.
> + */
> +static GHashTable *construct_alias_map(const BitmapMigrationNodeAliasList
> *bbm,
> + bool name_to_alias,
> + Error **errp)
> +{
> + GHashTable *alias_map;
> +
> + alias_map = g_hash_table_new_full(g_str_hash, g_str_equal,
> + g_free, free_alias_map_inner_node);
> +
> + for (; bbm; bbm = bbm->next) {
> + const BitmapMigrationNodeAlias *bmna = bbm->value;
> + const BitmapMigrationBitmapAliasList *bmbal;
> + AliasMapInnerNode *amin;
> + GHashTable *bitmaps_map;
> + const char *node_map_from, *node_map_to;
> +
> + if (!id_wellformed(bmna->alias)) {
> + error_setg(errp, "The node alias %s is not well-formed",
> + bmna->alias);
> + goto fail;
> + }
> +
> + if (name_to_alias) {
> + if (g_hash_table_contains(alias_map, bmna->node_name)) {
> + error_setg(errp, "The node name %s is mapped twice",
> + bmna->node_name);
> + goto fail;
> + }
> +
> + node_map_from = bmna->node_name;
> + node_map_to = bmna->alias;
> + } else {
> + if (g_hash_table_contains(alias_map, bmna->alias)) {
> + error_setg(errp, "The node alias %s is used twice",
> + bmna->alias);
> + goto fail;
> + }
> +
> + node_map_from = bmna->alias;
> + node_map_to = bmna->node_name;
> + }
> +
> + bitmaps_map = g_hash_table_new_full(g_str_hash, g_str_equal,
> + g_free, g_free);
> +
> + amin = g_new(AliasMapInnerNode, 1);
> + *amin = (AliasMapInnerNode){
> + .string = g_strdup(node_map_to),
> + .subtree = bitmaps_map,
> + };
> +
> + g_hash_table_insert(alias_map, g_strdup(node_map_from), amin);
> +
> + for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) {
> + const BitmapMigrationBitmapAlias *bmba = bmbal->value;
> + const char *bmap_map_from, *bmap_map_to;
> +
> + if (name_to_alias) {
> + bmap_map_from = bmba->name;
> + bmap_map_to = bmba->alias;
> +
> + if (g_hash_table_contains(bitmaps_map, bmba->name)) {
> + error_setg(errp, "The bitmap %s/%s is mapped twice",
> + bmna->node_name, bmba->name);
> + goto fail;
> + }
> + } else {
> + bmap_map_from = bmba->alias;
> + bmap_map_to = bmba->name;
> +
> + if (g_hash_table_contains(bitmaps_map, bmba->alias)) {
> + error_setg(errp, "The bitmap alias %s/%s is used twice",
> + bmna->alias, bmba->alias);
> + goto fail;
> + }
> + }
> +
> + g_hash_table_insert(bitmaps_map,
> + g_strdup(bmap_map_from),
> g_strdup(bmap_map_to));
> + }
> + }
> +
> + return alias_map;
> +
> +fail:
> + g_hash_table_destroy(alias_map);
> + return NULL;
> +}
> +
> +/**
> + * Run construct_alias_map() in both directions to check whether @bbm
> + * is valid.
> + * (This function is to be used by migration/migration.c to validate
> + * the user-specified block-bitmap-mapping migration parameter.)
> + *
> + * Returns true if and only if the mapping is valid.
> + */
> +bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList
> *bbm,
> + Error **errp)
> +{
> + GHashTable *alias_map;
> +
> + alias_map = construct_alias_map(bbm, true, errp);
> + if (!alias_map) {
> + return false;
> + }
> + g_hash_table_destroy(alias_map);
> +
> + alias_map = construct_alias_map(bbm, false, errp);
> + if (!alias_map) {
> + return false;
> + }
> + g_hash_table_destroy(alias_map);
> +
> + return true;
> +}
> +
> void init_dirty_bitmap_incoming_migration(void)
> {
> qemu_mutex_init(&finish_lock);
> @@ -191,11 +351,11 @@ static void send_bitmap_header(QEMUFile *f,
> DirtyBitmapMigBitmapState *dbms,
> qemu_put_bitmap_flags(f, flags);
>
> if (flags & DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME) {
> - qemu_put_counted_string(f, dbms->node_name);
> + qemu_put_counted_string(f, dbms->node_alias);
> }
>
> if (flags & DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME) {
> - qemu_put_counted_string(f, bdrv_dirty_bitmap_name(bitmap));
> + qemu_put_counted_string(f, dbms->bitmap_alias);
> }
> }
>
> @@ -263,17 +423,25 @@ static void dirty_bitmap_mig_cleanup(void)
> QSIMPLEQ_REMOVE_HEAD(&dirty_bitmap_mig_state.dbms_list, entry);
> bdrv_dirty_bitmap_set_busy(dbms->bitmap, false);
> bdrv_unref(dbms->bs);
> + g_free(dbms->node_alias);
> + g_free(dbms->bitmap_alias);
> g_free(dbms);
> }
> }
>
> /* Called with iothread lock taken. */
> -static int add_bitmaps_to_list(BlockDriverState *bs, const char *bs_name)
> +static int add_bitmaps_to_list(BlockDriverState *bs, const char *bs_name,
> + GHashTable *alias_map)
> {
> BdrvDirtyBitmap *bitmap;
> DirtyBitmapMigBitmapState *dbms;
> + GHashTable *bitmap_aliases;
> + const char *node_alias, *bitmap_name, *bitmap_alias;
> Error *local_err = NULL;
>
> + /* When an alias map is given, @bs_name must be @bs's node name */
> + assert(!alias_map || !strcmp(bs_name, bdrv_get_node_name(bs)));
> +
> FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
> if (bdrv_dirty_bitmap_name(bitmap)) {
> break;
> @@ -283,21 +451,39 @@ static int add_bitmaps_to_list(BlockDriverState *bs,
> const char *bs_name)
> return 0;
> }
>
> + bitmap_name = bdrv_dirty_bitmap_name(bitmap);
> +
> if (!bs_name || strcmp(bs_name, "") == 0) {
> error_report("Bitmap '%s' in unnamed node can't be migrated",
> - bdrv_dirty_bitmap_name(bitmap));
> + bitmap_name);
> return -1;
> }
>
> - if (bs_name[0] == '#') {
> + if (alias_map) {
> + const AliasMapInnerNode *amin = g_hash_table_lookup(alias_map,
> bs_name);
> +
> + if (!amin) {
> + /* Skip bitmaps on nodes with no alias */
> + return 0;
> + }
> +
> + node_alias = amin->string;
> + bitmap_aliases = amin->subtree;
> + } else {
> + node_alias = bs_name;
> + bitmap_aliases = NULL;
> + }
> +
> + if (node_alias[0] == '#') {
> error_report("Bitmap '%s' in a node with auto-generated "
> "name '%s' can't be migrated",
> - bdrv_dirty_bitmap_name(bitmap), bs_name);
> + bitmap_name, node_alias);
> return -1;
> }
>
> FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
> - if (!bdrv_dirty_bitmap_name(bitmap)) {
> + bitmap_name = bdrv_dirty_bitmap_name(bitmap);
> + if (!bitmap_name) {
> continue;
> }
>
> @@ -306,12 +492,23 @@ static int add_bitmaps_to_list(BlockDriverState *bs,
> const char *bs_name)
> return -1;
> }
>
> + if (bitmap_aliases) {
> + bitmap_alias = g_hash_table_lookup(bitmap_aliases, bitmap_name);
> + if (!bitmap_alias) {
> + /* Skip bitmaps with no alias */
> + continue;
> + }
> + } else {
> + bitmap_alias = bitmap_name;
> + }
> +
> bdrv_ref(bs);
> bdrv_dirty_bitmap_set_busy(bitmap, true);
>
> dbms = g_new0(DirtyBitmapMigBitmapState, 1);
> dbms->bs = bs;
> - dbms->node_name = bs_name;
> + dbms->node_alias = g_strdup(node_alias);
> + dbms->bitmap_alias = g_strdup(bitmap_alias);
> dbms->bitmap = bitmap;
> dbms->total_sectors = bdrv_nb_sectors(bs);
> dbms->sectors_per_chunk = CHUNK_SIZE * 8 *
> @@ -337,43 +534,52 @@ static int init_dirty_bitmap_migration(void)
> DirtyBitmapMigBitmapState *dbms;
> GHashTable *handled_by_blk = g_hash_table_new(NULL, NULL);
> BlockBackend *blk;
> + const MigrationParameters *mig_params =
> &migrate_get_current()->parameters;
> + GHashTable *alias_map = NULL;
> +
> + if (mig_params->has_block_bitmap_mapping) {
> + alias_map = construct_alias_map(mig_params->block_bitmap_mapping,
> true,
> + &error_abort);
> + }
>
> dirty_bitmap_mig_state.bulk_completed = false;
> dirty_bitmap_mig_state.prev_bs = NULL;
> dirty_bitmap_mig_state.prev_bitmap = NULL;
> dirty_bitmap_mig_state.no_bitmaps = false;
>
> - /*
> - * Use blockdevice name for direct (or filtered) children of named block
> - * backends.
> - */
> - for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
> - const char *name = blk_name(blk);
> -
> - if (!name || strcmp(name, "") == 0) {
> - continue;
> - }
> + if (!alias_map) {
> + /*
> + * Use blockdevice name for direct (or filtered) children of named
> block
> + * backends.
> + */
> + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
> + const char *name = blk_name(blk);
>
> - bs = blk_bs(blk);
> + if (!name || strcmp(name, "") == 0) {
> + continue;
> + }
>
> - /* Skip filters without bitmaps */
> - while (bs && bs->drv && bs->drv->is_filter &&
> - !bdrv_has_named_bitmaps(bs))
> - {
> - if (bs->backing) {
> - bs = bs->backing->bs;
> - } else if (bs->file) {
> - bs = bs->file->bs;
> - } else {
> - bs = NULL;
> + bs = blk_bs(blk);
> +
> + /* Skip filters without bitmaps */
> + while (bs && bs->drv && bs->drv->is_filter &&
> + !bdrv_has_named_bitmaps(bs))
> + {
> + if (bs->backing) {
> + bs = bs->backing->bs;
> + } else if (bs->file) {
> + bs = bs->file->bs;
> + } else {
> + bs = NULL;
> + }
> }
> - }
>
> - if (bs && bs->drv && !bs->drv->is_filter) {
> - if (add_bitmaps_to_list(bs, name)) {
> - goto fail;
> + if (bs && bs->drv && !bs->drv->is_filter) {
> + if (add_bitmaps_to_list(bs, name, NULL)) {
> + goto fail;
> + }
> + g_hash_table_add(handled_by_blk, bs);
> }
> - g_hash_table_add(handled_by_blk, bs);
> }
> }
>
> @@ -382,7 +588,7 @@ static int init_dirty_bitmap_migration(void)
> continue;
> }
>
> - if (add_bitmaps_to_list(bs, bdrv_get_node_name(bs))) {
> + if (add_bitmaps_to_list(bs, bdrv_get_node_name(bs), alias_map)) {
> goto fail;
> }
> }
> @@ -397,11 +603,17 @@ static int init_dirty_bitmap_migration(void)
> }
>
> g_hash_table_destroy(handled_by_blk);
> + if (alias_map) {
> + g_hash_table_destroy(alias_map);
> + }
>
> return 0;
>
> fail:
> g_hash_table_destroy(handled_by_blk);
> + if (alias_map) {
> + g_hash_table_destroy(alias_map);
> + }
> dirty_bitmap_mig_cleanup();
>
> return -1;
> @@ -666,8 +878,10 @@ static int dirty_bitmap_load_bits(QEMUFile *f,
> DirtyBitmapLoadState *s)
> return 0;
> }
>
> -static int dirty_bitmap_load_header(QEMUFile *f, DirtyBitmapLoadState *s)
> +static int dirty_bitmap_load_header(QEMUFile *f, DirtyBitmapLoadState *s,
> + GHashTable *alias_map)
> {
> + GHashTable *bitmap_alias_map = NULL;
> Error *local_err = NULL;
> bool nothing;
> s->flags = qemu_get_bitmap_flags(f);
> @@ -676,25 +890,68 @@ static int dirty_bitmap_load_header(QEMUFile *f,
> DirtyBitmapLoadState *s)
> nothing = s->flags == (s->flags & DIRTY_BITMAP_MIG_FLAG_EOS);
>
> if (s->flags & DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME) {
> - if (!qemu_get_counted_string(f, s->node_name)) {
> - error_report("Unable to read node name string");
> + if (!qemu_get_counted_string(f, s->node_alias)) {
> + error_report("Unable to read node alias string");
> return -EINVAL;
> }
> - s->bs = bdrv_lookup_bs(s->node_name, s->node_name, &local_err);
> +
> + if (alias_map) {
> + const AliasMapInnerNode *amin;
> +
> + amin = g_hash_table_lookup(alias_map, s->node_alias);
> + if (!amin) {
> + error_report("Error: Unknown node alias '%s'",
> s->node_alias);
> + return -EINVAL;
> + }
> +
> + bitmap_alias_map = amin->subtree;
> + s->bs = bdrv_lookup_bs(NULL, amin->string, &local_err);
> + } else {
> + s->bs = bdrv_lookup_bs(s->node_alias, s->node_alias, &local_err);
> + }
> if (!s->bs) {
> error_report_err(local_err);
> return -EINVAL;
> }
> - } else if (!s->bs && !nothing) {
> + } else if (s->bs) {
> + if (alias_map) {
> + const AliasMapInnerNode *amin;
> +
> + /* Must be present in the map, or s->bs would not be set */
> + amin = g_hash_table_lookup(alias_map, s->node_alias);
> + assert(amin != NULL);
> +
> + bitmap_alias_map = amin->subtree;
> + }
> + } else if (!nothing) {
> error_report("Error: block device name is not set");
> return -EINVAL;
> }
>
> + assert(!!alias_map == !!bitmap_alias_map);
> +
> if (s->flags & DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME) {
> - if (!qemu_get_counted_string(f, s->bitmap_name)) {
> + const char *bitmap_name;
> +
> + if (!qemu_get_counted_string(f, s->bitmap_alias)) {
> error_report("Unable to read bitmap name string");
> return -EINVAL;
> }
> +
> + if (bitmap_alias_map) {
> + bitmap_name = g_hash_table_lookup(bitmap_alias_map,
> + s->bitmap_alias);
> + if (!bitmap_name) {
> + error_report("Error: Unknown bitmap alias '%s' on node '%s' "
> + "(alias '%s')", s->bitmap_alias,
> s->bs->node_name,
> + s->node_alias);
> + return -EINVAL;
> + }
> + } else {
> + bitmap_name = s->bitmap_alias;
> + }
> +
> + g_strlcpy(s->bitmap_name, bitmap_name, sizeof(s->bitmap_name));
> s->bitmap = bdrv_find_dirty_bitmap(s->bs, s->bitmap_name);
>
> /* bitmap may be NULL here, it wouldn't be an error if it is the
> @@ -702,7 +959,7 @@ static int dirty_bitmap_load_header(QEMUFile *f,
> DirtyBitmapLoadState *s)
> if (!s->bitmap && !(s->flags & DIRTY_BITMAP_MIG_FLAG_START)) {
> error_report("Error: unknown dirty bitmap "
> "'%s' for block device '%s'",
> - s->bitmap_name, s->node_name);
> + s->bitmap_name, s->bs->node_name);
> return -EINVAL;
> }
> } else if (!s->bitmap && !nothing) {
> @@ -715,6 +972,8 @@ static int dirty_bitmap_load_header(QEMUFile *f,
> DirtyBitmapLoadState *s)
>
> static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id)
> {
> + GHashTable *alias_map = NULL;
> + const MigrationParameters *mig_params =
> &migrate_get_current()->parameters;
> static DirtyBitmapLoadState s;
> int ret = 0;
>
> @@ -724,10 +983,15 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque,
> int version_id)
> return -EINVAL;
> }
>
> + if (mig_params->has_block_bitmap_mapping) {
> + alias_map = construct_alias_map(mig_params->block_bitmap_mapping,
> + false, &error_abort);
> + }
> +
> do {
> - ret = dirty_bitmap_load_header(f, &s);
> + ret = dirty_bitmap_load_header(f, &s, alias_map);
> if (ret < 0) {
> - return ret;
> + goto fail;
> }
>
> if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) {
> @@ -743,12 +1007,17 @@ static int dirty_bitmap_load(QEMUFile *f, void
> *opaque, int version_id)
> }
>
> if (ret) {
> - return ret;
> + goto fail;
> }
> } while (!(s.flags & DIRTY_BITMAP_MIG_FLAG_EOS));
>
> trace_dirty_bitmap_load_success();
> - return 0;
> + ret = 0;
> +fail:
> + if (alias_map) {
> + g_hash_table_destroy(alias_map);
> + }
> + return ret;
> }
>
> static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque)
> diff --git a/migration/migration.c b/migration/migration.c
> index 2ed9923227..bcf7c3bf76 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -36,6 +36,7 @@
> #include "block/block.h"
> #include "qapi/error.h"
> #include "qapi/clone-visitor.h"
> +#include "qapi/qapi-visit-migration.h"
> #include "qapi/qapi-visit-sockets.h"
> #include "qapi/qapi-commands-migration.h"
> #include "qapi/qapi-events-migration.h"
> @@ -832,6 +833,13 @@ MigrationParameters *qmp_query_migrate_parameters(Error
> **errp)
> params->has_announce_step = true;
> params->announce_step = s->parameters.announce_step;
>
> + if (s->parameters.has_block_bitmap_mapping) {
> + params->has_block_bitmap_mapping = true;
> + params->block_bitmap_mapping =
> + QAPI_CLONE(BitmapMigrationNodeAliasList,
> + s->parameters.block_bitmap_mapping);
> + }
> +
> return params;
> }
>
> @@ -1297,6 +1305,13 @@ static bool migrate_params_check(MigrationParameters
> *params, Error **errp)
> "is invalid, it must be in the range of 1 to 10000 ms");
> return false;
> }
> +
> + if (params->has_block_bitmap_mapping &&
> + !check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping,
> errp)) {
> + error_prepend(errp, "Invalid mapping given for block-bitmap-mapping:
> ");
> + return false;
> + }
> +
> return true;
> }
>
> @@ -1391,6 +1406,11 @@ static void
> migrate_params_test_apply(MigrateSetParameters *params,
> if (params->has_announce_step) {
> dest->announce_step = params->announce_step;
> }
> +
> + if (params->has_block_bitmap_mapping) {
> + dest->has_block_bitmap_mapping = true;
> + dest->block_bitmap_mapping = params->block_bitmap_mapping;
> + }
> }
>
> static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
> @@ -1503,6 +1523,16 @@ static void migrate_params_apply(MigrateSetParameters
> *params, Error **errp)
> if (params->has_announce_step) {
> s->parameters.announce_step = params->announce_step;
> }
> +
> + if (params->has_block_bitmap_mapping) {
> + qapi_free_BitmapMigrationNodeAliasList(
> + s->parameters.block_bitmap_mapping);
> +
> + s->parameters.has_block_bitmap_mapping = true;
> + s->parameters.block_bitmap_mapping =
> + QAPI_CLONE(BitmapMigrationNodeAliasList,
> + params->block_bitmap_mapping);
> + }
> }
>
> void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
> diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
> index ae4b6a4246..7711726fd2 100644
> --- a/monitor/hmp-cmds.c
> +++ b/monitor/hmp-cmds.c
> @@ -469,6 +469,32 @@ void hmp_info_migrate_parameters(Monitor *mon, const
> QDict *qdict)
> monitor_printf(mon, "%s: '%s'\n",
> MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ),
> params->tls_authz);
> +
> + if (params->has_block_bitmap_mapping) {
> + const BitmapMigrationNodeAliasList *bmnal;
> +
> + monitor_printf(mon, "%s:\n",
> + MigrationParameter_str(
> + MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING));
> +
> + for (bmnal = params->block_bitmap_mapping;
> + bmnal;
> + bmnal = bmnal->next)
> + {
> + const BitmapMigrationNodeAlias *bmna = bmnal->value;
> + const BitmapMigrationBitmapAliasList *bmbal;
> +
> + monitor_printf(mon, " '%s' -> '%s'\n",
> + bmna->node_name, bmna->alias);
> +
> + for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) {
> + const BitmapMigrationBitmapAlias *bmba = bmbal->value;
> +
> + monitor_printf(mon, " '%s' -> '%s'\n",
> + bmba->name, bmba->alias);
> + }
> + }
> + }
> }
>
> qapi_free_MigrationParameters(params);
> @@ -1384,6 +1410,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const
> QDict *qdict)
> p->has_announce_step = true;
> visit_type_size(v, param, &p->announce_step, &err);
> break;
> + case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING:
> + error_setg(&err, "The block-bitmap-mapping parameter can only be set
> "
> + "through QMP");
> + break;
> default:
> assert(0);
> }
> --
> 2.26.2
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK