From 225ffb360161b2bbe599d8db6d7a38fde6bf2bfc Mon Sep 17 00:00:00 2001 From: Jeffrey Cody Date: Wed, 21 Mar 2012 21:55:08 +0100 Subject: [PATCH 41/55] qmp: add block_stream command RH-Author: Jeffrey Cody Message-id: <4334a90afb0c3d57ea41e3b69db6c08e73dccbd9.1332362400.git.jcody@redhat.com> Patchwork-id: 38892 O-Subject: [RHEL6.3 qemu-kvm PATCH v8 41/54] qmp: add block_stream command Bugzilla: 582475 RH-Acked-by: Paolo Bonzini RH-Acked-by: Marcelo Tosatti RH-Acked-by: Kevin Wolf From: Stefan Hajnoczi Add the block_stream command, which starts copy backing file contents into the image file. Also add the BLOCK_JOB_COMPLETED QMP event which is emitted when image streaming completes. Later patches add control over the background copy speed, cancelation, and querying running streaming operations. Signed-off-by: Stefan Hajnoczi Acked-by: Luiz Capitulino Signed-off-by: Kevin Wolf (cherry picked from commit 12bd451fe0be83474910bb63b5874458141d4230) Signed-off-by: Stefan Hajnoczi Signed-off-by: Anthony Liguori Signed-off-by: Jeff Cody --- QMP/qmp-events.txt | 29 ++++++++++++++++++++++ blockdev.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ blockdev.h | 1 + monitor.c | 3 ++ monitor.h | 1 + qemu-monitor.hx | 43 ++++++++++++++++++++++++++++++++ qerror.c | 4 +++ qerror.h | 3 ++ trace-events | 4 +++ 9 files changed, 157 insertions(+), 0 deletions(-) Signed-off-by: Michal Novotny --- QMP/qmp-events.txt | 29 ++++++++++++++++++++++ blockdev.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ blockdev.h | 1 + monitor.c | 3 ++ monitor.h | 1 + qemu-monitor.hx | 43 ++++++++++++++++++++++++++++++++ qerror.c | 4 +++ qerror.h | 3 ++ trace-events | 4 +++ 9 files changed, 157 insertions(+), 0 deletions(-) diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt index 42f6992..cb43ba7 100644 --- a/QMP/qmp-events.txt +++ b/QMP/qmp-events.txt @@ -297,3 +297,32 @@ Example: Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is followed respectively by the RESET, SHUTDOWN, or STOP events. + + +BLOCK_JOB_COMPLETED +------------------- + +Emitted when a block job has completed. + +Data: + +- "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) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) +- "error": Error message (json-string, optional) + Only present on failure. This 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. + +Example: + +{ "event": "BLOCK_JOB_COMPLETED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 10737418240, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } diff --git a/blockdev.c b/blockdev.c index cdd3ea3..d60ee40 100644 --- a/blockdev.c +++ b/blockdev.c @@ -13,9 +13,11 @@ #include "qerror.h" #include "qemu-option.h" #include "qemu-config.h" +#include "qemu-objects.h" #include "sysemu.h" #include "block_int.h" #include "qmp-commands.h" +#include "trace.h" struct drivelist drives = QTAILQ_HEAD_INITIALIZER(drives); DriveInfo *extboot_drive = NULL; @@ -998,3 +1000,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 171401c..6194bda 100644 --- a/blockdev.h +++ b/blockdev.h @@ -71,5 +71,6 @@ int do_change_block(Monitor *mon, const char *device, const char *filename, const char *fmt); int simple_drive_add(Monitor *mon, const QDict *qdict, QObject **ret_data); int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_block_stream(Monitor *mon, const QDict *qdict, QObject **ret_data); #endif diff --git a/monitor.c b/monitor.c index b714e99..ff688a0 100644 --- a/monitor.c +++ b/monitor.c @@ -478,6 +478,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) case QEVENT_DEVICE_TRAY_MOVED: event_name = "DEVICE_TRAY_MOVED"; break; + case QEVENT_BLOCK_JOB_COMPLETED: + event_name = "BLOCK_JOB_COMPLETED"; + break; case QEVENT_RH_SPICE_INITIALIZED: event_name = RFQDN_REDHAT "SPICE_INITIALIZED"; break; diff --git a/monitor.h b/monitor.h index b4481cf..f315a92 100644 --- a/monitor.h +++ b/monitor.h @@ -39,6 +39,7 @@ typedef enum MonitorEvent { QEVENT_SPICE_INITIALIZED, QEVENT_SPICE_DISCONNECTED, QEVENT_DEVICE_TRAY_MOVED, + QEVENT_BLOCK_JOB_COMPLETED, QEVENT_RH_SPICE_INITIALIZED, QEVENT_RH_SPICE_DISCONNECTED, QEVENT_SUSPEND, diff --git a/qemu-monitor.hx b/qemu-monitor.hx index 95b4635..839e77b 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -2036,6 +2036,49 @@ Example: EQMP + { + .name = "block_stream", + .args_type = "device:B,base:s?", + .params = "device [base]", + .help = "background copy backing file into a block device", + .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 +and the BLOCK_JOB_COMPLETED event is emitted. + +Arguments: + +- device: the device name (json-string) +- base: the common backing file name (json-string, optional) + +Returns: + +Nothing on success. +If streaming is already active on this device, DeviceInUse. +If device does not exist, DeviceNotFound. +If image streaming is not supported by this device, NotSupported. + +EQMP + HXCOMM Keep the 'info' command at the end! HXCOMM This is required for the QMP documentation layout. diff --git a/qerror.c b/qerror.c index 29d6577..724a7bf 100644 --- a/qerror.c +++ b/qerror.c @@ -174,6 +174,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 27de1da..18e4b95 100644 --- a/qerror.h +++ b/qerror.h @@ -152,6 +152,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/trace-events b/trace-events index bf83d7a..f226c1c 100644 --- a/trace-events +++ b/trace-events @@ -66,6 +66,10 @@ disable bdrv_co_do_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, i disable stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d" disable stream_start(void *bs, void *base, void *s, void *co, void *opaque) "bs %p base %p s %p co %p opaque %p" +# blockdev.c +disable block_stream_cb(void *bs, void *job, int ret) "bs %p job %p ret %d" +disable do_block_stream(void *bs, void *job) "bs %p job %p" + # hw/virtio-blk.c disable virtio_blk_req_complete(void *req, int status) "req %p status %d" disable virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" -- 1.7.7.6