diff options
Diffstat (limited to 'qemu-img.c')
-rw-r--r-- | qemu-img.c | 107 |
1 files changed, 91 insertions, 16 deletions
diff --git a/qemu-img.c b/qemu-img.c index 2d30682f12..d7e846e607 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -78,6 +78,7 @@ enum { OPTION_ENABLE = 272, OPTION_DISABLE = 273, OPTION_MERGE = 274, + OPTION_BITMAPS = 275, }; typedef enum OutputFormat { @@ -191,6 +192,7 @@ static void QEMU_NORETURN help(void) " hiding corruption that has already occurred.\n" "\n" "Parameters to convert subcommand:\n" + " '--bitmaps' copies all top-level persistent bitmaps to destination\n" " '-m' specifies how many coroutines work in parallel during the convert\n" " process (defaults to 8)\n" " '-W' allow to write to the target out of order rather than sequential\n" @@ -1638,6 +1640,24 @@ out4: return ret; } +/* Convenience wrapper around qmp_block_dirty_bitmap_merge */ +static void do_dirty_bitmap_merge(const char *dst_node, const char *dst_name, + const char *src_node, const char *src_name, + Error **errp) +{ + BlockDirtyBitmapMergeSource *merge_src; + BlockDirtyBitmapMergeSourceList *list; + + merge_src = g_new0(BlockDirtyBitmapMergeSource, 1); + merge_src->type = QTYPE_QDICT; + merge_src->u.external.node = g_strdup(src_node); + merge_src->u.external.name = g_strdup(src_name); + list = g_new0(BlockDirtyBitmapMergeSourceList, 1); + list->value = merge_src; + qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp); + qapi_free_BlockDirtyBitmapMergeSourceList(list); +} + enum ImgConvertBlockStatus { BLK_DATA, BLK_ZERO, @@ -2121,6 +2141,39 @@ static int convert_do_copy(ImgConvertState *s) return s->ret; } +static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst) +{ + BdrvDirtyBitmap *bm; + Error *err = NULL; + + FOR_EACH_DIRTY_BITMAP(src, bm) { + const char *name; + + if (!bdrv_dirty_bitmap_get_persistence(bm)) { + continue; + } + name = bdrv_dirty_bitmap_name(bm); + qmp_block_dirty_bitmap_add(dst->node_name, name, + true, bdrv_dirty_bitmap_granularity(bm), + true, true, + true, !bdrv_dirty_bitmap_enabled(bm), + &err); + if (err) { + error_reportf_err(err, "Failed to create bitmap %s: ", name); + return -1; + } + + do_dirty_bitmap_merge(dst->node_name, name, src->node_name, name, + &err); + if (err) { + error_reportf_err(err, "Failed to populate bitmap %s: ", name); + return -1; + } + } + + return 0; +} + #define MAX_BUF_SECTORS 32768 static int img_convert(int argc, char **argv) @@ -2142,6 +2195,7 @@ static int img_convert(int argc, char **argv) int64_t ret = -EINVAL; bool force_share = false; bool explict_min_sparse = false; + bool bitmaps = false; ImgConvertState s = (ImgConvertState) { /* Need at least 4k of zeros for sparse detection */ @@ -2161,6 +2215,7 @@ static int img_convert(int argc, char **argv) {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {"salvage", no_argument, 0, OPTION_SALVAGE}, {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, + {"bitmaps", no_argument, 0, OPTION_BITMAPS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", @@ -2286,6 +2341,9 @@ static int img_convert(int argc, char **argv) */ s.has_zero_init = true; break; + case OPTION_BITMAPS: + bitmaps = true; + break; } } @@ -2347,7 +2405,6 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } - /* ret is still -EINVAL until here */ ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); if (ret < 0) { @@ -2507,6 +2564,20 @@ static int img_convert(int argc, char **argv) } } + /* Determine if bitmaps need copying */ + if (bitmaps) { + if (s.src_num > 1) { + error_report("Copying bitmaps only possible with single source"); + ret = -1; + goto out; + } + if (!bdrv_supports_persistent_dirty_bitmap(blk_bs(s.src[0]))) { + error_report("Source lacks bitmap support"); + ret = -1; + goto out; + } + } + /* * The later open call will need any decryption secrets, and * bdrv_create() will purge "opts", so extract them now before @@ -2515,9 +2586,7 @@ static int img_convert(int argc, char **argv) if (!skip_create) { open_opts = qdict_new(); qemu_opt_foreach(opts, img_add_key_secrets, open_opts, &error_abort); - } - if (!skip_create) { /* Create the new image */ ret = bdrv_create(drv, out_filename, opts, &local_err); if (ret < 0) { @@ -2555,6 +2624,13 @@ static int img_convert(int argc, char **argv) } out_bs = blk_bs(s.target); + if (bitmaps && !bdrv_supports_persistent_dirty_bitmap(out_bs)) { + error_report("Format driver '%s' does not support bitmaps", + out_bs->drv->format_name); + ret = -1; + goto out; + } + if (s.compressed && !block_driver_can_compress(out_bs->drv)) { error_report("Compression not supported for this file format"); ret = -1; @@ -2614,6 +2690,12 @@ static int img_convert(int argc, char **argv) } ret = convert_do_copy(&s); + + /* Now copy the bitmaps */ + if (bitmaps && ret == 0) { + ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs); + } + out: if (!ret) { qemu_progress_print(100, 0); @@ -4714,21 +4796,11 @@ static int img_bitmap(int argc, char **argv) qmp_block_dirty_bitmap_disable(bs->node_name, bitmap, &err); op = "disable"; break; - case BITMAP_MERGE: { - BlockDirtyBitmapMergeSource *merge_src; - BlockDirtyBitmapMergeSourceList *list; - - merge_src = g_new0(BlockDirtyBitmapMergeSource, 1); - merge_src->type = QTYPE_QDICT; - merge_src->u.external.node = g_strdup(src_bs->node_name); - merge_src->u.external.name = g_strdup(act->src); - list = g_new0(BlockDirtyBitmapMergeSourceList, 1); - list->value = merge_src; - qmp_block_dirty_bitmap_merge(bs->node_name, bitmap, list, &err); - qapi_free_BlockDirtyBitmapMergeSourceList(list); + case BITMAP_MERGE: + do_dirty_bitmap_merge(bs->node_name, bitmap, src_bs->node_name, + act->src, &err); op = "merge"; break; - } default: g_assert_not_reached(); } @@ -5302,6 +5374,9 @@ static int img_measure(int argc, char **argv) if (output_format == OFORMAT_HUMAN) { printf("required size: %" PRIu64 "\n", info->required); printf("fully allocated size: %" PRIu64 "\n", info->fully_allocated); + if (info->has_bitmaps) { + printf("bitmaps size: %" PRIu64 "\n", info->bitmaps); + } } else { dump_json_block_measure_info(info); } |