Add the block_stream command, which starts copy backing file contents into the image file. Later patches add control over the background copy speed, cancelation, and querying running streaming operations.
Signed-off-by: Stefan Hajnoczi <stefa...@linux.vnet.ibm.com> --- blockdev.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ blockdev.h | 1 + hmp-commands.hx | 14 +++++++++++ monitor.c | 3 ++ monitor.h | 1 + qerror.c | 4 +++ qerror.h | 3 ++ qmp-commands.hx | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ trace-events | 4 +++ 9 files changed, 164 insertions(+), 0 deletions(-) diff --git a/blockdev.c b/blockdev.c index 1dd0f23..de911de 100644 --- a/blockdev.c +++ b/blockdev.c @@ -13,8 +13,10 @@ #include "qerror.h" #include "qemu-option.h" #include "qemu-config.h" +#include "qemu-objects.h" #include "sysemu.h" #include "block_int.h" +#include "trace.h" static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); @@ -782,3 +784,70 @@ int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data) return 0; } + +static QObject *qobject_from_block_job(BlockJob *job) +{ + return qobject_from_jsonf("{ 'type': %s," + "'device': %s," + "'len': %" PRId64 "," + "'offset': %" PRId64 "," + "'speed': %" PRId64 " }", + job->job_type->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed); +} + +static void block_stream_cb(void *opaque, int ret) +{ + BlockDriverState *bs = opaque; + QObject *obj; + + trace_block_stream_cb(bs, bs->job, ret); + + assert(bs->job); + obj = qobject_from_block_job(bs->job); + if (ret < 0) { + QDict *dict = qobject_to_qdict(obj); + qdict_put(dict, "error", qstring_from_str(strerror(-ret))); + } + + monitor_protocol_event(QEVENT_BLOCK_JOB_COMPLETED, obj); + qobject_decref(obj); +} + +int do_block_stream(Monitor *mon, const QDict *params, QObject **ret_data) +{ + const char *device = qdict_get_str(params, "device"); + const char *base = qdict_get_try_str(params, "base"); + BlockDriverState *bs; + int ret; + + bs = bdrv_find(device); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, device); + return -1; + } + + /* Base device not supported */ + if (base) { + qerror_report(QERR_NOT_SUPPORTED); + return -1; + } + + ret = stream_start(bs, NULL, block_stream_cb, bs); + if (ret < 0) { + switch (ret) { + case -EBUSY: + qerror_report(QERR_DEVICE_IN_USE, device); + return -1; + default: + qerror_report(QERR_NOT_SUPPORTED); + return -1; + } + } + + trace_do_block_stream(bs, bs->job); + return 0; +} diff --git a/blockdev.h b/blockdev.h index 3587786..ad98d37 100644 --- a/blockdev.h +++ b/blockdev.h @@ -65,5 +65,6 @@ int do_change_block(Monitor *mon, const char *device, int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data); int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_block_stream(Monitor *mon, const QDict *qdict, QObject **ret_data); #endif diff --git a/hmp-commands.hx b/hmp-commands.hx index 05a1498..2aeb2e0 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -70,6 +70,20 @@ but should be used with extreme caution. Note that this command only resizes image files, it can not resize block devices like LVM volumes. ETEXI + { + .name = "block_stream", + .args_type = "device:B,base:s?", + .params = "device [base]", + .help = "copy data from a backing file into a block device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_stream, + }, + +STEXI +@item block_stream +@findex block_stream +Copy data from a backing file into a block device. +ETEXI { .name = "eject", diff --git a/monitor.c b/monitor.c index ffda0fe..38addcf 100644 --- a/monitor.c +++ b/monitor.c @@ -482,6 +482,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) case QEVENT_SPICE_DISCONNECTED: event_name = "SPICE_DISCONNECTED"; break; + case QEVENT_BLOCK_JOB_COMPLETED: + event_name = "BLOCK_JOB_COMPLETED"; + break; default: abort(); break; diff --git a/monitor.h b/monitor.h index 4f2d328..135c927 100644 --- a/monitor.h +++ b/monitor.h @@ -35,6 +35,7 @@ typedef enum MonitorEvent { QEVENT_SPICE_CONNECTED, QEVENT_SPICE_INITIALIZED, QEVENT_SPICE_DISCONNECTED, + QEVENT_BLOCK_JOB_COMPLETED, QEVENT_MAX, } MonitorEvent; diff --git a/qerror.c b/qerror.c index 68998d4..f531afa 100644 --- a/qerror.c +++ b/qerror.c @@ -162,6 +162,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "No '%(bus)' bus found for device '%(device)'", }, { + .error_fmt = QERR_NOT_SUPPORTED, + .desc = "Not supported", + }, + { .error_fmt = QERR_OPEN_FILE_FAILED, .desc = "Could not open '%(filename)'", }, diff --git a/qerror.h b/qerror.h index d4bfcfd..3988610 100644 --- a/qerror.h +++ b/qerror.h @@ -141,6 +141,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_NO_BUS_FOR_DEVICE \ "{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }" +#define QERR_NOT_SUPPORTED \ + "{ 'class': 'NotSupported', 'data': {} }" + #define QERR_OPEN_FILE_FAILED \ "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }" diff --git a/qmp-commands.hx b/qmp-commands.hx index 4328e8b..31cde4b 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -685,6 +685,71 @@ Example: EQMP { + .name = "block_stream", + .args_type = "device:B,base:s?", + .params = "device [base]", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_stream, + }, + +SQMP +block_stream +------------ + +Copy data from a backing file into a block device. + +The block streaming operation is performed in the background until the entire +backing file has been copied. This command returns immediately once streaming +has started. The status of ongoing block streaming operations can be checked +with query-block-jobs. The operation can be stopped before it has completed +using the block_job_cancel command. + +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 +file chain instead of flattening the entire image. + +On successful completion the image file is updated to drop the backing file. + +Arguments: + +- device: device name (json-string) +- base: common backing file (json-string, optional) + +Errors: + +DeviceInUse: streaming is already active on this device +DeviceNotFound: device name is invalid +NotSupported: image streaming is not supported by this device + +Events: + +On completion the BLOCK_JOB_COMPLETED event is raised with the following +fields: + +- type: job type ("stream" for image streaming, json-string) +- device: device name (json-string) +- len: maximum progress value (json-int) +- offset: current progress value (json-int) +- speed: rate limit, bytes per second (json-int) +- error: error message (json-string, only on error) + +The completion event is raised both on success and on failure. On +success offset is equal to len. On failure offset and len can be +used to indicate at which point the operation failed. + +On failure the error field contains a human-readable error message. There are +no semantics other than that streaming has failed and clients should not try +to interpret the error string. + +Examples: + +-> { "execute": "block_stream", "arguments": { "device": "virtio0" } } +<- { "return": {} } + +EQMP + + { .name = "blockdev-snapshot-sync", .args_type = "device:B,snapshot-file:s?,format:s?", .params = "device [new-image-file] [format]", diff --git a/trace-events b/trace-events index 487d560..15a6b7a 100644 --- a/trace-events +++ b/trace-events @@ -75,6 +75,10 @@ bdrv_co_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, int64_t clus stream_one_iteration(void *s, int64_t sector_num, int max_sectors) "s %p sector_num %"PRId64" max_sectors %d" stream_start(void *bs, void *base, void *s, void *co, void *opaque) "bs %p base %p s %p co %p opaque %p" +# blockdev.c +block_stream_cb(void *bs, void *job, int ret) "bs %p job %p ret %d" +do_block_stream(void *bs, void *job) "bs %p job %p" + # hw/virtio-blk.c virtio_blk_req_complete(void *req, int status) "req %p status %d" virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" -- 1.7.7