From c95a07ad1cd2db64c1b25c385e253e98a6b97ac5 Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Fri, 12 Aug 2011 15:38:21 +0200 Subject: [PATCH 13/15] qmp: add block_job_set_speed command RH-Author: Anthony Liguori Message-id: <1313163503-2523-14-git-send-email-aliguori@redhat.com> Patchwork-id: 31334 O-Subject: [RHEL6.2 qemu PATCH 13/15] qmp: add block_job_set_speed command Bugzilla: 633370 RH-Acked-by: Marcelo Tosatti RH-Acked-by: Kevin Wolf RH-Acked-by: Orit Wasserman RH-Acked-by: Christoph Hellwig From: Stefan Hajnoczi The block_job_set_speed command sets a throughput limit on an active image streaming operation. This can be used to isolate the streaming operation and control the amount of I/O bandwidth it consumes. The command synopsis is as follows: block_job_set_speed ------------------- Set maximum speed for a background block operation. This is a per-block device command that can only be issued when there is an active block job. Throttling can be disabled by setting the speed to 0. Arguments: - device: device name (json-string) - value: maximum speed, in bytes per second (json-int) Errors: DeviceNotActive: streaming is not active on this device NotSupported: job type does not support speed setting Example: -> { "execute": "block_job_set_speed", "arguments": { "device": "virtio0", "value": 1024 } } Signed-off-by: Stefan Hajnoczi Signed-off-by: Anthony Liguori Bugzilla: 633370 --- blockdev.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- blockdev.h | 2 + qemu-monitor.hx | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) Signed-off-by: Michal Novotny --- blockdev.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- blockdev.h | 2 + qemu-monitor.hx | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) diff --git a/blockdev.c b/blockdev.c index cb1ee85..6712dc6 100644 --- a/blockdev.c +++ b/blockdev.c @@ -53,12 +53,20 @@ static const int if_max_devs[IF_COUNT] = { [IF_SCSI] = 7, }; +enum { + SLICE_TIME_MS = 100, /* 100 ms rate-limiting slice time */ +}; + typedef struct StreamState { MonitorCompletion *cancel_cb; void *cancel_opaque; int64_t offset; /* current position in block device */ BlockDriverState *bs; QEMUTimer *timer; + int64_t bytes_per_sec; /* rate limit */ + int64_t bytes_per_slice; /* rate limit scaled to slice */ + int64_t slice_end_time; /* when this slice finishes */ + int64_t slice_start_offset; /* offset when slice started */ QLIST_ENTRY(StreamState) list; } StreamState; @@ -73,7 +81,7 @@ static QObject *stream_get_qobject(StreamState *s) return qobject_from_jsonf("{ 'device': %s, 'type': 'stream', " "'offset': %" PRId64 ", 'len': %" PRId64 ", " "'speed': %" PRId64 " }", - name, s->offset, len, (int64_t)0); + name, s->offset, len, s->bytes_per_sec); } static void stream_mon_event(StreamState *s, int ret) @@ -110,6 +118,27 @@ static void stream_complete(StreamState *s, int ret) stream_free(s); } +static void stream_schedule_next_iteration(StreamState *s) +{ + int64_t next = qemu_get_clock(rt_clock); + + /* New slice */ + if (next >= s->slice_end_time) { + s->slice_end_time = next + SLICE_TIME_MS; + s->slice_start_offset = s->offset; + } + + /* Throttle */ + if (s->bytes_per_slice && + s->offset - s->slice_start_offset >= s->bytes_per_slice) { + next = s->slice_end_time; + s->slice_end_time = next + SLICE_TIME_MS; + s->slice_start_offset += s->bytes_per_slice; + } + + qemu_mod_timer(s->timer, next); +} + static void stream_cb(void *opaque, int nb_sectors) { StreamState *s = opaque; @@ -127,7 +156,7 @@ static void stream_cb(void *opaque, int nb_sectors) } else if (s->cancel_cb) { stream_free(s); } else { - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock)); + stream_schedule_next_iteration(s); } } @@ -210,6 +239,20 @@ static int stream_stop(const char *device, MonitorCompletion *cb, void *opaque) return 0; } +static int stream_set_speed(const char *device, int64_t bytes_per_sec) +{ + StreamState *s = stream_find(device); + + if (!s) { + qerror_report(QERR_DEVICE_NOT_ACTIVE, device); + return -1; + } + + s->bytes_per_sec = bytes_per_sec; + s->bytes_per_slice = bytes_per_sec * SLICE_TIME_MS / 1000LL; + return 0; +} + /* * We automatically delete the drive when a device using it gets * unplugged. Questionable feature, but we can't just drop it. @@ -872,7 +915,7 @@ static void monitor_print_block_stream(Monitor *mon, const QObject *data) qdict_get_str(stream, "device"), qdict_get_int(stream, "offset"), qdict_get_int(stream, "len"), - (int64_t)0); + qdict_get_int(stream, "speed")); } static void monitor_print_block_job(QObject *obj, void *opaque) @@ -923,6 +966,20 @@ int do_block_job_cancel(Monitor *mon, const QDict *params, return stream_stop(device, cb, opaque); } +int do_block_job_set_speed(Monitor *mon, const QDict *params, + QObject **ret_data) +{ + const char *device = qdict_get_str(params, "device"); + int64_t value; + + value = qdict_get_int(params, "value"); + if (value < 0) { + value = 0; + } + + return stream_set_speed(device, value); +} + static int eject_device(Monitor *mon, BlockDriverState *bs, int force) { if (!force) { diff --git a/blockdev.h b/blockdev.h index 71f9c46..5b98377 100644 --- a/blockdev.h +++ b/blockdev.h @@ -78,5 +78,7 @@ void do_info_block_jobs(Monitor *mon, QObject **ret_data); int do_block_stream(Monitor *mon, const QDict *params, QObject **ret_data); int do_block_job_cancel(Monitor *mon, const QDict *params, MonitorCompletion cb, void *opaque); +int do_block_job_set_speed(Monitor *mon, const QDict *params, + QObject **ret_data); #endif diff --git a/qemu-monitor.hx b/qemu-monitor.hx index f78fda0..8377df7 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -215,6 +215,47 @@ Examples: EQMP { + .name = "block_job_set_speed", + .args_type = "device:B,value:o", + .params = "device value", + .help = "Set the maximum speed for a background block operation", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_job_set_speed, + }, + +STEXI +@item block_job_set_speed @var{device} @var{value} +@findex block_job_set_speed +Set the maximum speed for a background block operation. +ETEXI +SQMP +block_job_set_speed +------------------- + +Set maximum speed for a background block operation. + +This is a per-block device command that can only be issued +when there is an active block job. + +Throttling can be disabled by setting the speed to 0. + +Arguments: + +- device: device name (json-string) +- value: maximum speed, in bytes per second (json-int) + +Errors: +DeviceNotActive: streaming is not active on this device +NotSupported: job type does not support speed setting + +Example: + +-> { "execute": "block_job_set_speed", + "arguments": { "device": "virtio0", "value": 1024 } } + +EQMP + + { .name = "q|quit", .args_type = "", .params = "", -- 1.7.4.4