提交 12bd451f 编写于 作者: S Stefan Hajnoczi 提交者: Kevin Wolf

qmp: add block_stream command

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: NStefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Acked-by: NLuiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
上级 5094a6c0
...@@ -264,3 +264,32 @@ Example: ...@@ -264,3 +264,32 @@ Example:
Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
followed respectively by the RESET, SHUTDOWN, or STOP events. 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 } }
...@@ -13,9 +13,11 @@ ...@@ -13,9 +13,11 @@
#include "qerror.h" #include "qerror.h"
#include "qemu-option.h" #include "qemu-option.h"
#include "qemu-config.h" #include "qemu-config.h"
#include "qemu-objects.h"
#include "sysemu.h" #include "sysemu.h"
#include "block_int.h" #include "block_int.h"
#include "qmp-commands.h" #include "qmp-commands.h"
#include "trace.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
...@@ -897,3 +899,68 @@ void qmp_block_resize(const char *device, int64_t size, Error **errp) ...@@ -897,3 +899,68 @@ void qmp_block_resize(const char *device, int64_t size, Error **errp)
break; break;
} }
} }
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);
}
void qmp_block_stream(const char *device, bool has_base,
const char *base, Error **errp)
{
BlockDriverState *bs;
int ret;
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
/* Base device not supported */
if (base) {
error_set(errp, QERR_NOT_SUPPORTED);
return;
}
ret = stream_start(bs, NULL, block_stream_cb, bs);
if (ret < 0) {
switch (ret) {
case -EBUSY:
error_set(errp, QERR_DEVICE_IN_USE, device);
return;
default:
error_set(errp, QERR_NOT_SUPPORTED);
return;
}
}
trace_qmp_block_stream(bs, bs->job);
}
...@@ -69,6 +69,19 @@ but should be used with extreme caution. Note that this command only ...@@ -69,6 +69,19 @@ but should be used with extreme caution. Note that this command only
resizes image files, it can not resize block devices like LVM volumes. resizes image files, it can not resize block devices like LVM volumes.
ETEXI ETEXI
{
.name = "block_stream",
.args_type = "device:B,base:s?",
.params = "device [base]",
.help = "copy data from a backing file into a block device",
.mhandler.cmd = hmp_block_stream,
},
STEXI
@item block_stream
@findex block_stream
Copy data from a backing file into a block device.
ETEXI
{ {
.name = "eject", .name = "eject",
......
...@@ -783,3 +783,14 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) ...@@ -783,3 +783,14 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
qdict_get_int(qdict, "iops_wr"), &err); qdict_get_int(qdict, "iops_wr"), &err);
hmp_handle_error(mon, &err); hmp_handle_error(mon, &err);
} }
void hmp_block_stream(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
const char *base = qdict_get_try_str(qdict, "base");
qmp_block_stream(device, base != NULL, base, &error);
hmp_handle_error(mon, &error);
}
...@@ -54,5 +54,6 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict); ...@@ -54,5 +54,6 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict);
void hmp_eject(Monitor *mon, const QDict *qdict); void hmp_eject(Monitor *mon, const QDict *qdict);
void hmp_change(Monitor *mon, const QDict *qdict); void hmp_change(Monitor *mon, const QDict *qdict);
void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
void hmp_block_stream(Monitor *mon, const QDict *qdict);
#endif #endif
...@@ -479,6 +479,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) ...@@ -479,6 +479,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
case QEVENT_SPICE_DISCONNECTED: case QEVENT_SPICE_DISCONNECTED:
event_name = "SPICE_DISCONNECTED"; event_name = "SPICE_DISCONNECTED";
break; break;
case QEVENT_BLOCK_JOB_COMPLETED:
event_name = "BLOCK_JOB_COMPLETED";
break;
default: default:
abort(); abort();
break; break;
......
...@@ -36,6 +36,7 @@ typedef enum MonitorEvent { ...@@ -36,6 +36,7 @@ typedef enum MonitorEvent {
QEVENT_SPICE_CONNECTED, QEVENT_SPICE_CONNECTED,
QEVENT_SPICE_INITIALIZED, QEVENT_SPICE_INITIALIZED,
QEVENT_SPICE_DISCONNECTED, QEVENT_SPICE_DISCONNECTED,
QEVENT_BLOCK_JOB_COMPLETED,
QEVENT_MAX, QEVENT_MAX,
} MonitorEvent; } MonitorEvent;
......
...@@ -1434,3 +1434,34 @@ ...@@ -1434,3 +1434,34 @@
{ 'command': 'block_set_io_throttle', { 'command': 'block_set_io_throttle',
'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int' } } 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int' } }
# @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.
#
# @device: the device name
#
# @base: #optional the common backing file name
#
# 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
#
# Since: 1.1
##
{ 'command': 'block_stream', 'data': { 'device': 'str', '*base': 'str' } }
...@@ -196,6 +196,10 @@ static const QErrorStringTable qerror_table[] = { ...@@ -196,6 +196,10 @@ static const QErrorStringTable qerror_table[] = {
.error_fmt = QERR_NO_BUS_FOR_DEVICE, .error_fmt = QERR_NO_BUS_FOR_DEVICE,
.desc = "No '%(bus)' bus found for device '%(device)'", .desc = "No '%(bus)' bus found for device '%(device)'",
}, },
{
.error_fmt = QERR_NOT_SUPPORTED,
.desc = "Not supported",
},
{ {
.error_fmt = QERR_OPEN_FILE_FAILED, .error_fmt = QERR_OPEN_FILE_FAILED,
.desc = "Could not open '%(filename)'", .desc = "Could not open '%(filename)'",
......
...@@ -168,6 +168,9 @@ QError *qobject_to_qerror(const QObject *obj); ...@@ -168,6 +168,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_NO_BUS_FOR_DEVICE \ #define QERR_NO_BUS_FOR_DEVICE \
"{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }" "{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }"
#define QERR_NOT_SUPPORTED \
"{ 'class': 'NotSupported', 'data': {} }"
#define QERR_OPEN_FILE_FAILED \ #define QERR_OPEN_FILE_FAILED \
"{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }" "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
......
...@@ -648,6 +648,12 @@ Example: ...@@ -648,6 +648,12 @@ Example:
EQMP EQMP
{
.name = "block_stream",
.args_type = "device:B,base:s?",
.mhandler.cmd_new = qmp_marshal_input_block_stream,
},
{ {
.name = "blockdev-snapshot-sync", .name = "blockdev-snapshot-sync",
.args_type = "device:B,snapshot-file:s,format:s?", .args_type = "device:B,snapshot-file:s,format:s?",
......
...@@ -74,6 +74,10 @@ bdrv_co_do_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, int64_t c ...@@ -74,6 +74,10 @@ bdrv_co_do_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, int64_t c
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" 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"
stream_start(void *bs, void *base, void *s, void *co, void *opaque) "bs %p base %p s %p co %p opaque %p" 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"
qmp_block_stream(void *bs, void *job) "bs %p job %p"
# hw/virtio-blk.c # hw/virtio-blk.c
virtio_blk_req_complete(void *req, int status) "req %p status %d" virtio_blk_req_complete(void *req, int status) "req %p status %d"
virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" virtio_blk_rw_complete(void *req, int ret) "req %p ret %d"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册