[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 4/8] qmp: add block_stream command
From: |
Stefan Hajnoczi |
Subject: |
[Qemu-devel] [PATCH 4/8] qmp: add block_stream command |
Date: |
Thu, 27 Oct 2011 16:22:51 +0100 |
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 <address@hidden>
---
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
address@hidden block_stream
address@hidden 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
- [Qemu-devel] [PATCH 0/8] block: generic image streaming, Stefan Hajnoczi, 2011/10/27
- [Qemu-devel] [PATCH 7/8] qmp: add query-block-jobs, Stefan Hajnoczi, 2011/10/27
- [Qemu-devel] [PATCH 8/8] test: add image streaming test cases, Stefan Hajnoczi, 2011/10/27
- [Qemu-devel] [PATCH 2/8] block: add BlockJob interface for long-running operations, Stefan Hajnoczi, 2011/10/27
- [Qemu-devel] [PATCH 4/8] qmp: add block_stream command,
Stefan Hajnoczi <=
- [Qemu-devel] [PATCH 6/8] qmp: add block_job_cancel command, Stefan Hajnoczi, 2011/10/27
- [Qemu-devel] [PATCH 1/8] coroutine: add co_sleep_ns() coroutine sleep function, Stefan Hajnoczi, 2011/10/27
- [Qemu-devel] [PATCH 5/8] qmp: add block_job_set_speed command, Stefan Hajnoczi, 2011/10/27
- [Qemu-devel] [PATCH 3/8] block: add image streaming block job, Stefan Hajnoczi, 2011/10/27
- Re: [Qemu-devel] [PATCH 0/8] block: generic image streaming, Luiz Capitulino, 2011/10/27