mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 04:06:46 +00:00 
			
		
		
		
	bitmaps patches for 2021-02-12
- add 'transform' member to manipulate bitmaps across migration - work towards better error handling during bdrv_open -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEccLMIrHEYCkn0vOqp6FrSiUnQ2oFAmAnDQsACgkQp6FrSiUn Q2qc5Qf/SKVdpX4j7OnHF6sBuf/8LVWz4KazSqEU0ohazBJmafgJpH2EA5pXMXR4 frZDWeanGmhj1MjMkta/++uvEBU/TMpW2z98mZvjErteXdnRQAlII/hOCI+QZJvg viQ5t1EyrkyXzUePOjs+AwqA5KHWbCKt6QqyItQ78HvI23sw/fuvHj0G67KbVzXZ VcSrVr0J7PXnZV/hWfg+C+Nn9Ro9tsVdn79awLYVQ7/SDro3hzylpcHMQaHMK2oe mX4D2kNq7s21E27Zb6vlknUhQPkMdETk0gfEbpn7sTVMEc58GRLC7Tqfx7l0JIFK 5izVyA5vndKVxDGYPkbDK6VL2uDg4A== =+Epy -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ericb/tags/pull-bitmaps-2021-02-12' into staging bitmaps patches for 2021-02-12 - add 'transform' member to manipulate bitmaps across migration - work towards better error handling during bdrv_open # gpg: Signature made Fri 12 Feb 2021 23:19:39 GMT # gpg: using RSA key 71C2CC22B1C4602927D2F3AAA7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" [full] # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" [full] # gpg: aka "[jpeg image of size 6874]" [full] # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-bitmaps-2021-02-12: block: use return status of bdrv_append() block: return status from bdrv_append and friends qemu-iotests: 300: Add test case for modifying persistence of bitmap migration: dirty-bitmap: Allow control of bitmap persistence migration: dirty-bitmap: Use struct for alias map inner members Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
		
						commit
						392b9a74b9
					
				
							
								
								
									
										64
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								block.c
									
									
									
									
									
								
							| @ -2827,14 +2827,15 @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) | ||||
|  * Sets the bs->backing link of a BDS. A new reference is created; callers | ||||
|  * which don't need their own reference any more must call bdrv_unref(). | ||||
|  */ | ||||
| void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||||
|                          Error **errp) | ||||
| int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||||
|                         Error **errp) | ||||
| { | ||||
|     int ret = 0; | ||||
|     bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) && | ||||
|         bdrv_inherits_from_recursive(backing_hd, bs); | ||||
| 
 | ||||
|     if (bdrv_is_backing_chain_frozen(bs, child_bs(bs->backing), errp)) { | ||||
|         return; | ||||
|         return -EPERM; | ||||
|     } | ||||
| 
 | ||||
|     if (backing_hd) { | ||||
| @ -2853,15 +2854,22 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||||
| 
 | ||||
|     bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_of_bds, | ||||
|                                     bdrv_backing_role(bs), errp); | ||||
|     if (!bs->backing) { | ||||
|         ret = -EPERM; | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     /* If backing_hd was already part of bs's backing chain, and
 | ||||
|      * inherits_from pointed recursively to bs then let's update it to | ||||
|      * point directly to bs (else it will become NULL). */ | ||||
|     if (bs->backing && update_inherits_from) { | ||||
|     if (update_inherits_from) { | ||||
|         backing_hd->inherits_from = bs; | ||||
|     } | ||||
| 
 | ||||
| out: | ||||
|     bdrv_refresh_limits(bs, NULL); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -3110,7 +3118,6 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||||
|     int64_t total_size; | ||||
|     QemuOpts *opts = NULL; | ||||
|     BlockDriverState *bs_snapshot = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
| 
 | ||||
|     /* if snapshot, we create a temporary backing file and open it
 | ||||
| @ -3157,9 +3164,8 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||||
|      * order to be able to return one, we have to increase | ||||
|      * bs_snapshot's refcount here */ | ||||
|     bdrv_ref(bs_snapshot); | ||||
|     bdrv_append(bs_snapshot, bs, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|     ret = bdrv_append(bs_snapshot, bs, errp); | ||||
|     if (ret < 0) { | ||||
|         bs_snapshot = NULL; | ||||
|         goto out; | ||||
|     } | ||||
| @ -4532,9 +4538,9 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) | ||||
|  * With auto_skip=false the error is returned if from has a parent which should | ||||
|  * not be updated. | ||||
|  */ | ||||
| static void bdrv_replace_node_common(BlockDriverState *from, | ||||
|                                      BlockDriverState *to, | ||||
|                                      bool auto_skip, Error **errp) | ||||
| static int bdrv_replace_node_common(BlockDriverState *from, | ||||
|                                     BlockDriverState *to, | ||||
|                                     bool auto_skip, Error **errp) | ||||
| { | ||||
|     BdrvChild *c, *next; | ||||
|     GSList *list = NULL, *p; | ||||
| @ -4556,11 +4562,13 @@ static void bdrv_replace_node_common(BlockDriverState *from, | ||||
|             if (auto_skip) { | ||||
|                 continue; | ||||
|             } | ||||
|             ret = -EINVAL; | ||||
|             error_setg(errp, "Should not change '%s' link to '%s'", | ||||
|                        c->name, from->node_name); | ||||
|             goto out; | ||||
|         } | ||||
|         if (c->frozen) { | ||||
|             ret = -EPERM; | ||||
|             error_setg(errp, "Cannot change '%s' link to '%s'", | ||||
|                        c->name, from->node_name); | ||||
|             goto out; | ||||
| @ -4591,14 +4599,18 @@ static void bdrv_replace_node_common(BlockDriverState *from, | ||||
| 
 | ||||
|     bdrv_set_perm(to); | ||||
| 
 | ||||
|     ret = 0; | ||||
| 
 | ||||
| out: | ||||
|     g_slist_free(list); | ||||
|     bdrv_drained_end(from); | ||||
|     bdrv_unref(from); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||||
|                        Error **errp) | ||||
| int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||||
|                       Error **errp) | ||||
| { | ||||
|     return bdrv_replace_node_common(from, to, true, errp); | ||||
| } | ||||
| @ -4619,28 +4631,30 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||||
|  * parents of bs_top after bdrv_append() returns. If the caller needs to keep a | ||||
|  * reference of its own, it must call bdrv_ref(). | ||||
|  */ | ||||
| void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||||
|                  Error **errp) | ||||
| int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||||
|                 Error **errp) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
| 
 | ||||
|     bdrv_set_backing_hd(bs_new, bs_top, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|     int ret = bdrv_set_backing_hd(bs_new, bs_top, errp); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     bdrv_replace_node(bs_top, bs_new, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|     ret = bdrv_replace_node(bs_top, bs_new, errp); | ||||
|     if (ret < 0) { | ||||
|         bdrv_set_backing_hd(bs_new, NULL, &error_abort); | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     /* bs_new is now referenced by its new parents, we don't need the
 | ||||
|      * additional reference any more. */ | ||||
|     ret = 0; | ||||
| 
 | ||||
| out: | ||||
|     /*
 | ||||
|      * bs_new is now referenced by its new parents, we don't need the | ||||
|      * additional reference any more. | ||||
|      */ | ||||
|     bdrv_unref(bs_new); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static void bdrv_delete(BlockDriverState *bs) | ||||
|  | ||||
| @ -191,7 +191,8 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | ||||
|                                          BlockCopyState **bcs, | ||||
|                                          Error **errp) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     ERRP_GUARD(); | ||||
|     int ret; | ||||
|     BDRVBackupTopState *state; | ||||
|     BlockDriverState *top; | ||||
|     bool appended = false; | ||||
| @ -224,9 +225,9 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | ||||
|     bdrv_drained_begin(source); | ||||
| 
 | ||||
|     bdrv_ref(top); | ||||
|     bdrv_append(top, source, &local_err); | ||||
|     if (local_err) { | ||||
|         error_prepend(&local_err, "Cannot append backup-top filter: "); | ||||
|     ret = bdrv_append(top, source, errp); | ||||
|     if (ret < 0) { | ||||
|         error_prepend(errp, "Cannot append backup-top filter: "); | ||||
|         goto fail; | ||||
|     } | ||||
|     appended = true; | ||||
| @ -236,19 +237,18 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | ||||
|      * we want. | ||||
|      */ | ||||
|     state->active = true; | ||||
|     bdrv_child_refresh_perms(top, top->backing, &local_err); | ||||
|     if (local_err) { | ||||
|         error_prepend(&local_err, | ||||
|                       "Cannot set permissions for backup-top filter: "); | ||||
|     ret = bdrv_child_refresh_perms(top, top->backing, errp); | ||||
|     if (ret < 0) { | ||||
|         error_prepend(errp, "Cannot set permissions for backup-top filter: "); | ||||
|         goto fail; | ||||
|     } | ||||
| 
 | ||||
|     state->cluster_size = cluster_size; | ||||
|     state->bcs = block_copy_state_new(top->backing, state->target, | ||||
|                                       cluster_size, perf->use_copy_range, | ||||
|                                       write_flags, &local_err); | ||||
|     if (local_err) { | ||||
|         error_prepend(&local_err, "Cannot create block-copy-state: "); | ||||
|                                       write_flags, errp); | ||||
|     if (!state->bcs) { | ||||
|         error_prepend(errp, "Cannot create block-copy-state: "); | ||||
|         goto fail; | ||||
|     } | ||||
|     *bcs = state->bcs; | ||||
| @ -266,7 +266,6 @@ fail: | ||||
|     } | ||||
| 
 | ||||
|     bdrv_drained_end(source); | ||||
|     error_propagate(errp, local_err); | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| @ -254,7 +254,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|     BlockDriverState *iter; | ||||
|     BlockDriverState *commit_top_bs = NULL; | ||||
|     BlockDriverState *filtered_base; | ||||
|     Error *local_err = NULL; | ||||
|     int64_t base_size, top_size; | ||||
|     uint64_t base_perms, iter_shared_perms; | ||||
|     int ret; | ||||
| @ -312,10 +311,9 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
| 
 | ||||
|     commit_top_bs->total_sectors = top->total_sectors; | ||||
| 
 | ||||
|     bdrv_append(commit_top_bs, top, &local_err); | ||||
|     if (local_err) { | ||||
|     ret = bdrv_append(commit_top_bs, top, errp); | ||||
|     if (ret < 0) { | ||||
|         commit_top_bs = NULL; | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1569,7 +1569,6 @@ static BlockJob *mirror_start_job( | ||||
|     BlockDriverState *mirror_top_bs; | ||||
|     bool target_is_backing; | ||||
|     uint64_t target_perms, target_shared_perms; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
| 
 | ||||
|     if (granularity == 0) { | ||||
| @ -1618,12 +1617,11 @@ static BlockJob *mirror_start_job( | ||||
|      * it alive until block_job_create() succeeds even if bs has no parent. */ | ||||
|     bdrv_ref(mirror_top_bs); | ||||
|     bdrv_drained_begin(bs); | ||||
|     bdrv_append(mirror_top_bs, bs, &local_err); | ||||
|     ret = bdrv_append(mirror_top_bs, bs, errp); | ||||
|     bdrv_drained_end(bs); | ||||
| 
 | ||||
|     if (local_err) { | ||||
|     if (ret < 0) { | ||||
|         bdrv_unref(mirror_top_bs); | ||||
|         error_propagate(errp, local_err); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1432,6 +1432,7 @@ typedef struct ExternalSnapshotState { | ||||
| static void external_snapshot_prepare(BlkActionState *common, | ||||
|                                       Error **errp) | ||||
| { | ||||
|     int ret; | ||||
|     int flags = 0; | ||||
|     QDict *options = NULL; | ||||
|     Error *local_err = NULL; | ||||
| @ -1591,9 +1592,8 @@ static void external_snapshot_prepare(BlkActionState *common, | ||||
|      * can fail, so we need to do it in .prepare; undoing it for abort is | ||||
|      * always possible. */ | ||||
|     bdrv_ref(state->new_bs); | ||||
|     bdrv_append(state->new_bs, state->old_bs, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|     ret = bdrv_append(state->new_bs, state->old_bs, errp); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|     state->overlay_appended = true; | ||||
|  | ||||
| @ -356,10 +356,10 @@ int bdrv_create(BlockDriver *drv, const char* filename, | ||||
| int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); | ||||
| 
 | ||||
| BlockDriverState *bdrv_new(void); | ||||
| void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||||
|                  Error **errp); | ||||
| void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||||
|                        Error **errp); | ||||
| int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||||
|                 Error **errp); | ||||
| int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||||
|                       Error **errp); | ||||
| BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, | ||||
|                                    int flags, Error **errp); | ||||
| 
 | ||||
| @ -373,8 +373,8 @@ BdrvChild *bdrv_open_child(const char *filename, | ||||
|                            BdrvChildRole child_role, | ||||
|                            bool allow_none, Error **errp); | ||||
| BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); | ||||
| void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||||
|                          Error **errp); | ||||
| int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||||
|                         Error **errp); | ||||
| int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||||
|                            const char *bdref_key, Error **errp); | ||||
| BlockDriverState *bdrv_open(const char *filename, const char *reference, | ||||
|  | ||||
| @ -75,6 +75,8 @@ | ||||
| #include "qemu/id.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qapi-commands-migration.h" | ||||
| #include "qapi/qapi-visit-migration.h" | ||||
| #include "qapi/clone-visitor.h" | ||||
| #include "trace.h" | ||||
| 
 | ||||
| #define CHUNK_SIZE     (1 << 10) | ||||
| @ -148,6 +150,7 @@ typedef struct DBMLoadState { | ||||
|     BdrvDirtyBitmap *bitmap; | ||||
| 
 | ||||
|     bool before_vm_start_handled; /* set in dirty_bitmap_mig_before_vm_start */ | ||||
|     BitmapMigrationBitmapAlias *bmap_inner; | ||||
| 
 | ||||
|     /*
 | ||||
|      * cancelled | ||||
| @ -224,6 +227,7 @@ static GHashTable *construct_alias_map(const BitmapMigrationNodeAliasList *bbm, | ||||
|         AliasMapInnerNode *amin; | ||||
|         GHashTable *bitmaps_map; | ||||
|         const char *node_map_from, *node_map_to; | ||||
|         GDestroyNotify gdn; | ||||
| 
 | ||||
|         if (!id_wellformed(bmna->alias)) { | ||||
|             error_setg(errp, "The node alias '%s' is not well-formed", | ||||
| @ -263,8 +267,9 @@ static GHashTable *construct_alias_map(const BitmapMigrationNodeAliasList *bbm, | ||||
|             node_map_to = bmna->node_name; | ||||
|         } | ||||
| 
 | ||||
|         bitmaps_map = g_hash_table_new_full(g_str_hash, g_str_equal, | ||||
|                                             g_free, g_free); | ||||
|         gdn = (GDestroyNotify) qapi_free_BitmapMigrationBitmapAlias; | ||||
|         bitmaps_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, | ||||
|                                             gdn); | ||||
| 
 | ||||
|         amin = g_new(AliasMapInnerNode, 1); | ||||
|         *amin = (AliasMapInnerNode){ | ||||
| @ -276,7 +281,7 @@ static GHashTable *construct_alias_map(const BitmapMigrationNodeAliasList *bbm, | ||||
| 
 | ||||
|         for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) { | ||||
|             const BitmapMigrationBitmapAlias *bmba = bmbal->value; | ||||
|             const char *bmap_map_from, *bmap_map_to; | ||||
|             const char *bmap_map_from; | ||||
| 
 | ||||
|             if (strlen(bmba->alias) > UINT8_MAX) { | ||||
|                 error_setg(errp, | ||||
| @ -293,7 +298,6 @@ static GHashTable *construct_alias_map(const BitmapMigrationNodeAliasList *bbm, | ||||
| 
 | ||||
|             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", | ||||
| @ -302,7 +306,6 @@ static GHashTable *construct_alias_map(const BitmapMigrationNodeAliasList *bbm, | ||||
|                 } | ||||
|             } 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", | ||||
| @ -311,8 +314,8 @@ static GHashTable *construct_alias_map(const BitmapMigrationNodeAliasList *bbm, | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             g_hash_table_insert(bitmaps_map, | ||||
|                                 g_strdup(bmap_map_from), g_strdup(bmap_map_to)); | ||||
|             g_hash_table_insert(bitmaps_map, g_strdup(bmap_map_from), | ||||
|                                 QAPI_CLONE(BitmapMigrationBitmapAlias, bmba)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -527,6 +530,7 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, | ||||
|     } | ||||
| 
 | ||||
|     FOR_EACH_DIRTY_BITMAP(bs, bitmap) { | ||||
|         BitmapMigrationBitmapAliasTransform *bitmap_transform = NULL; | ||||
|         bitmap_name = bdrv_dirty_bitmap_name(bitmap); | ||||
|         if (!bitmap_name) { | ||||
|             continue; | ||||
| @ -538,11 +542,18 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, | ||||
|         } | ||||
| 
 | ||||
|         if (bitmap_aliases) { | ||||
|             bitmap_alias = g_hash_table_lookup(bitmap_aliases, bitmap_name); | ||||
|             if (!bitmap_alias) { | ||||
|             BitmapMigrationBitmapAlias *bmap_inner; | ||||
| 
 | ||||
|             bmap_inner = g_hash_table_lookup(bitmap_aliases, bitmap_name); | ||||
|             if (!bmap_inner) { | ||||
|                 /* Skip bitmaps with no alias */ | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             bitmap_alias = bmap_inner->alias; | ||||
|             if (bmap_inner->has_transform) { | ||||
|                 bitmap_transform = bmap_inner->transform; | ||||
|             } | ||||
|         } else { | ||||
|             if (strlen(bitmap_name) > UINT8_MAX) { | ||||
|                 error_report("Cannot migrate bitmap '%s' on node '%s': " | ||||
| @ -568,8 +579,15 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, | ||||
|         if (bdrv_dirty_bitmap_enabled(bitmap)) { | ||||
|             dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_ENABLED; | ||||
|         } | ||||
|         if (bdrv_dirty_bitmap_get_persistence(bitmap)) { | ||||
|             dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; | ||||
|         if (bitmap_transform && | ||||
|             bitmap_transform->has_persistent) { | ||||
|             if (bitmap_transform->persistent) { | ||||
|                 dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; | ||||
|             } | ||||
|         } else { | ||||
|             if (bdrv_dirty_bitmap_get_persistence(bitmap)) { | ||||
|                 dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         QSIMPLEQ_INSERT_TAIL(&s->dbms_list, dbms, entry); | ||||
| @ -777,6 +795,7 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s) | ||||
|     uint32_t granularity = qemu_get_be32(f); | ||||
|     uint8_t flags = qemu_get_byte(f); | ||||
|     LoadBitmapState *b; | ||||
|     bool persistent; | ||||
| 
 | ||||
|     if (s->cancelled) { | ||||
|         return 0; | ||||
| @ -801,7 +820,15 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s) | ||||
|         return -EINVAL; | ||||
|     } | ||||
| 
 | ||||
|     if (flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT) { | ||||
|     if (s->bmap_inner && | ||||
|         s->bmap_inner->has_transform && | ||||
|         s->bmap_inner->transform->has_persistent) { | ||||
|         persistent = s->bmap_inner->transform->persistent; | ||||
|     } else { | ||||
|         persistent = flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; | ||||
|     } | ||||
| 
 | ||||
|     if (persistent) { | ||||
|         bdrv_dirty_bitmap_set_persistence(s->bitmap, true); | ||||
|     } | ||||
| 
 | ||||
| @ -1074,14 +1101,19 @@ static int dirty_bitmap_load_header(QEMUFile *f, DBMLoadState *s, | ||||
| 
 | ||||
|         bitmap_name = s->bitmap_alias; | ||||
|         if (!s->cancelled && bitmap_alias_map) { | ||||
|             bitmap_name = g_hash_table_lookup(bitmap_alias_map, | ||||
|                                               s->bitmap_alias); | ||||
|             if (!bitmap_name) { | ||||
|             BitmapMigrationBitmapAlias *bmap_inner; | ||||
| 
 | ||||
|             bmap_inner = g_hash_table_lookup(bitmap_alias_map, s->bitmap_alias); | ||||
|             if (!bmap_inner) { | ||||
|                 error_report("Error: Unknown bitmap alias '%s' on node " | ||||
|                              "'%s' (alias '%s')", s->bitmap_alias, | ||||
|                              s->bs->node_name, s->node_alias); | ||||
|                 cancel_incoming_locked(s); | ||||
|             } else { | ||||
|                 bitmap_name = bmap_inner->name; | ||||
|             } | ||||
| 
 | ||||
|             s->bmap_inner = bmap_inner; | ||||
|         } | ||||
| 
 | ||||
|         if (!s->cancelled) { | ||||
|  | ||||
| @ -536,6 +536,19 @@ | ||||
|   'data': [ 'none', 'zlib', | ||||
|             { 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] } | ||||
| 
 | ||||
| ## | ||||
| # @BitmapMigrationBitmapAliasTransform: | ||||
| # | ||||
| # @persistent: If present, the bitmap will be made persistent | ||||
| #              or transient depending on this parameter. | ||||
| # | ||||
| # Since: 6.0 | ||||
| ## | ||||
| { 'struct': 'BitmapMigrationBitmapAliasTransform', | ||||
|   'data': { | ||||
|       '*persistent': 'bool' | ||||
|   } } | ||||
| 
 | ||||
| ## | ||||
| # @BitmapMigrationBitmapAlias: | ||||
| # | ||||
| @ -544,12 +557,16 @@ | ||||
| # @alias: An alias name for migration (for example the bitmap name on | ||||
| #         the opposite site). | ||||
| # | ||||
| # @transform: Allows the modification of the migrated bitmap. | ||||
| #             (since 6.0) | ||||
| # | ||||
| # Since: 5.2 | ||||
| ## | ||||
| { 'struct': 'BitmapMigrationBitmapAlias', | ||||
|   'data': { | ||||
|       'name': 'str', | ||||
|       'alias': 'str' | ||||
|       'alias': 'str', | ||||
|       '*transform': 'BitmapMigrationBitmapAliasTransform' | ||||
|   } } | ||||
| 
 | ||||
| ## | ||||
|  | ||||
| @ -600,6 +600,99 @@ class TestCrossAliasMigration(TestDirtyBitmapMigration): | ||||
|         self.verify_dest_has_all_bitmaps() | ||||
|         self.verify_dest_error(None) | ||||
| 
 | ||||
| class TestAliasTransformMigration(TestDirtyBitmapMigration): | ||||
|     """ | ||||
|     Tests the 'transform' option which modifies bitmap persistence on migration. | ||||
|     """ | ||||
| 
 | ||||
|     src_node_name = 'node-a' | ||||
|     dst_node_name = 'node-b' | ||||
|     src_bmap_name = 'bmap-a' | ||||
|     dst_bmap_name = 'bmap-b' | ||||
| 
 | ||||
|     def setUp(self) -> None: | ||||
|         TestDirtyBitmapMigration.setUp(self) | ||||
| 
 | ||||
|         # Now create another block device and let both have two bitmaps each | ||||
|         result = self.vm_a.qmp('blockdev-add', | ||||
|                                node_name='node-b', driver='null-co', | ||||
|                                read_zeroes=False) | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm_b.qmp('blockdev-add', | ||||
|                                node_name='node-a', driver='null-co', | ||||
|                                read_zeroes=False) | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         bmaps_to_add = (('node-a', 'bmap-b'), | ||||
|                         ('node-b', 'bmap-a'), | ||||
|                         ('node-b', 'bmap-b')) | ||||
| 
 | ||||
|         for (node, bmap) in bmaps_to_add: | ||||
|             result = self.vm_a.qmp('block-dirty-bitmap-add', | ||||
|                                    node=node, name=bmap) | ||||
|             self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def transform_mapping() -> BlockBitmapMapping: | ||||
|         return [ | ||||
|             { | ||||
|                 'node-name': 'node-a', | ||||
|                 'alias': 'node-a', | ||||
|                 'bitmaps': [ | ||||
|                     { | ||||
|                         'name': 'bmap-a', | ||||
|                         'alias': 'bmap-a', | ||||
|                         'transform': | ||||
|                             { | ||||
|                                 'persistent': True | ||||
|                             } | ||||
|                     }, | ||||
|                     { | ||||
|                         'name': 'bmap-b', | ||||
|                         'alias': 'bmap-b' | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 'node-name': 'node-b', | ||||
|                 'alias': 'node-b', | ||||
|                 'bitmaps': [ | ||||
|                     { | ||||
|                         'name': 'bmap-a', | ||||
|                         'alias': 'bmap-a' | ||||
|                     }, | ||||
|                     { | ||||
|                         'name': 'bmap-b', | ||||
|                         'alias': 'bmap-b' | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         ] | ||||
| 
 | ||||
|     def verify_dest_bitmap_state(self) -> None: | ||||
|         bitmaps = self.vm_b.query_bitmaps() | ||||
| 
 | ||||
|         for node in bitmaps: | ||||
|             bitmaps[node] = sorted(((bmap['name'], bmap['persistent']) for bmap in bitmaps[node])) | ||||
| 
 | ||||
|         self.assertEqual(bitmaps, | ||||
|                          {'node-a': [('bmap-a', True), ('bmap-b', False)], | ||||
|                           'node-b': [('bmap-a', False), ('bmap-b', False)]}) | ||||
| 
 | ||||
|     def test_transform_on_src(self) -> None: | ||||
|         self.set_mapping(self.vm_a, self.transform_mapping()) | ||||
| 
 | ||||
|         self.migrate() | ||||
|         self.verify_dest_bitmap_state() | ||||
|         self.verify_dest_error(None) | ||||
| 
 | ||||
|     def test_transform_on_dst(self) -> None: | ||||
|         self.set_mapping(self.vm_b, self.transform_mapping()) | ||||
| 
 | ||||
|         self.migrate() | ||||
|         self.verify_dest_bitmap_state() | ||||
|         self.verify_dest_error(None) | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     iotests.main(supported_protocols=['file']) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| ..................................... | ||||
| ....................................... | ||||
| ---------------------------------------------------------------------- | ||||
| Ran 37 tests | ||||
| Ran 39 tests | ||||
| 
 | ||||
| OK | ||||
|  | ||||
| @ -101,7 +101,7 @@ static BlockDriverState *pass_through_node(const char *name) | ||||
|  */ | ||||
| static void test_update_perm_tree(void) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
| 
 | ||||
|     BlockBackend *root = blk_new(qemu_get_aio_context(), | ||||
|                                  BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ, | ||||
| @ -114,8 +114,8 @@ static void test_update_perm_tree(void) | ||||
|     bdrv_attach_child(filter, bs, "child", &child_of_bds, | ||||
|                       BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); | ||||
| 
 | ||||
|     bdrv_append(filter, bs, &local_err); | ||||
|     error_free_or_abort(&local_err); | ||||
|     ret = bdrv_append(filter, bs, NULL); | ||||
|     g_assert_cmpint(ret, <, 0); | ||||
| 
 | ||||
|     blk_unref(root); | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell