This adds the 'top' parameter to the 'block-stream' QMP command and checks that its value is valid before passing it to stream_start().
Signed-off-by: Alberto Garcia <be...@igalia.com> --- blockdev.c | 19 +++++++++++++++---- hmp.c | 2 +- include/qapi/qmp/qerror.h | 3 +++ qapi/block-core.json | 18 ++++++++++++++---- qmp-commands.hx | 2 +- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/blockdev.c b/blockdev.c index 06628ca..2404f89 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2099,6 +2099,7 @@ static void block_job_cb(void *opaque, int ret) void qmp_block_stream(const char *device, bool has_base, const char *base, + bool has_top, const char *top, bool has_backing_file, const char *backing_file, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, @@ -2106,6 +2107,7 @@ void qmp_block_stream(const char *device, { BlockDriverState *bs; BlockDriverState *base_bs = NULL; + BlockDriverState *top_bs; AioContext *aio_context; Error *local_err = NULL; const char *base_name = NULL; @@ -2114,7 +2116,7 @@ void qmp_block_stream(const char *device, on_error = BLOCKDEV_ON_ERROR_REPORT; } - bs = bdrv_find(device); + top_bs = bs = bdrv_find(device); if (!bs) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; @@ -2123,12 +2125,21 @@ void qmp_block_stream(const char *device, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) { + if (has_top) { + top_bs = bdrv_find_backing_image(bs, top); + if (top_bs == NULL) { + error_set(errp, QERR_TOP_NOT_FOUND, top); + goto out; + } + assert(bdrv_get_aio_context(top_bs) == aio_context); + } + + if (bdrv_op_is_blocked(top_bs, BLOCK_OP_TYPE_STREAM, errp)) { goto out; } if (has_base) { - base_bs = bdrv_find_backing_image(bs, base); + base_bs = bdrv_find_backing_image(top_bs, base); if (base_bs == NULL) { error_set(errp, QERR_BASE_NOT_FOUND, base); goto out; @@ -2148,7 +2159,7 @@ void qmp_block_stream(const char *device, /* backing_file string overrides base bs filename */ base_name = has_backing_file ? backing_file : base_name; - stream_start(bs, NULL, base_bs, base_name, has_speed ? speed : 0, + stream_start(bs, top_bs, base_bs, base_name, has_speed ? speed : 0, on_error, block_job_cb, bs, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hmp.c b/hmp.c index b47f331..28f7adb 100644 --- a/hmp.c +++ b/hmp.c @@ -1239,7 +1239,7 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) const char *base = qdict_get_try_str(qdict, "base"); int64_t speed = qdict_get_try_int(qdict, "speed", 0); - qmp_block_stream(device, base != NULL, base, false, NULL, + qmp_block_stream(device, base != NULL, base, false, NULL, false, NULL, qdict_haskey(qdict, "speed"), speed, true, BLOCKDEV_ON_ERROR_REPORT, &error); diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 986260f..d075f78 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -127,6 +127,9 @@ void qerror_report_err(Error *err); #define QERR_SET_PASSWD_FAILED \ ERROR_CLASS_GENERIC_ERROR, "Could not set password" +#define QERR_TOP_NOT_FOUND \ + ERROR_CLASS_GENERIC_ERROR, "Top '%s' not found" + #define QERR_UNDEFINED_ERROR \ ERROR_CLASS_GENERIC_ERROR, "An undefined error has occurred" diff --git a/qapi/block-core.json b/qapi/block-core.json index a3fdaf0..3073be6 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1013,6 +1013,9 @@ # with query-block-jobs. The operation can be stopped before it has completed # using the block-job-cancel command. # +# Data is copied to the top image, which defaults to the active layer if no other +# file is selected. +# # If a base file is specified then sectors are not copied from that base file and # its backing chain. When streaming completes the image file will have the base # file as its backing file. This can be used to stream a subset of the backing @@ -1025,8 +1028,14 @@ # # @base: #optional the common backing file name # -# @backing-file: #optional The backing file string to write into the active -# layer. This filename is not validated. +# @top: #optional Top image, only sectors below this image are streamed +# into it. +# +# If not specified, the top image is the active layer. +# (Since 2.3) +# +# @backing-file: #optional The backing file string to write into the top +# image. This filename is not validated. # # If a pathname string is such that it cannot be # resolved by QEMU, that means that subsequent QMP or @@ -1052,8 +1061,9 @@ # Since: 1.1 ## { 'command': 'block-stream', - 'data': { 'device': 'str', '*base': 'str', '*backing-file': 'str', - '*speed': 'int', '*on-error': 'BlockdevOnError' } } + 'data': { 'device': 'str', '*base': 'str', '*top': 'str', + '*backing-file': 'str', '*speed': 'int', + '*on-error': 'BlockdevOnError' } } ## # @block-job-set-speed: diff --git a/qmp-commands.hx b/qmp-commands.hx index a85d847..a02f19f 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -978,7 +978,7 @@ EQMP { .name = "block-stream", - .args_type = "device:B,base:s?,speed:o?,backing-file:s?,on-error:s?", + .args_type = "device:B,base:s?,top:s?,speed:o?,backing-file:s?,on-error:s?", .mhandler.cmd_new = qmp_marshal_input_block_stream, }, -- 2.1.4