From patchwork Wed Jul 27 13:44:51 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 107095 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id B342EB6F6F for ; Thu, 28 Jul 2011 00:42:28 +1000 (EST) Received: from localhost ([::1]:38308 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qm4RP-0002jW-57 for incoming@patchwork.ozlabs.org; Wed, 27 Jul 2011 09:46:27 -0400 Received: from eggs.gnu.org ([140.186.70.92]:33459) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qm4QB-0007M1-EI for qemu-devel@nongnu.org; Wed, 27 Jul 2011 09:45:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Qm4Q9-0004Zf-9A for qemu-devel@nongnu.org; Wed, 27 Jul 2011 09:45:11 -0400 Received: from mtagate1.uk.ibm.com ([194.196.100.161]:53123) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qm4Q8-0004ZE-U4 for qemu-devel@nongnu.org; Wed, 27 Jul 2011 09:45:09 -0400 Received: from d06nrmr1507.portsmouth.uk.ibm.com (d06nrmr1507.portsmouth.uk.ibm.com [9.149.38.233]) by mtagate1.uk.ibm.com (8.13.1/8.13.1) with ESMTP id p6RDj8RC003534 for ; Wed, 27 Jul 2011 13:45:08 GMT Received: from d06av09.portsmouth.uk.ibm.com (d06av09.portsmouth.uk.ibm.com [9.149.37.250]) by d06nrmr1507.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p6RDj70k2252926 for ; Wed, 27 Jul 2011 14:45:07 +0100 Received: from d06av09.portsmouth.uk.ibm.com (loopback [127.0.0.1]) by d06av09.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p6RDj7r1016323 for ; Wed, 27 Jul 2011 07:45:07 -0600 Received: from stefanha-thinkpad.ibm.com ([9.78.66.144]) by d06av09.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p6RDj1DK016013; Wed, 27 Jul 2011 07:45:07 -0600 From: Stefan Hajnoczi To: Date: Wed, 27 Jul 2011 14:44:51 +0100 Message-Id: <1311774295-8696-12-git-send-email-stefanha@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1311774295-8696-1-git-send-email-stefanha@linux.vnet.ibm.com> References: <1311774295-8696-1-git-send-email-stefanha@linux.vnet.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 194.196.100.161 Cc: Kevin Wolf , Anthony Liguori , Stefan Hajnoczi , Adam Litke Subject: [Qemu-devel] [PATCH 11/15] qmp: add block_job_set_speed command X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- blockdev.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- blockdev.h | 2 + hmp-commands.hx | 14 ++++++++++++ qmp-commands.hx | 35 ++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 3 deletions(-) diff --git a/blockdev.c b/blockdev.c index 422b43b..a044830 100644 --- a/blockdev.c +++ b/blockdev.c @@ -51,12 +51,20 @@ static const int if_max_devs[IF_COUNT] = { [IF_SCSI] = 7, }; +enum { + SLICE_TIME_NS = 100000000, /* 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; @@ -71,7 +79,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) @@ -107,6 +115,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_ns(rt_clock); + + /* New slice */ + if (next >= s->slice_end_time) { + s->slice_end_time = next + SLICE_TIME_NS; + 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_NS; + 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; @@ -124,7 +153,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_ns(rt_clock)); + stream_schedule_next_iteration(s); } } @@ -202,6 +231,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_NS / 1000000000LL; + return 0; +} + /* * We automatically delete the drive when a device using it gets * unplugged. Questionable feature, but we can't just drop it. @@ -814,7 +857,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")); } void monitor_print_block_jobs(Monitor *mon, const QObject *data) @@ -863,6 +906,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 0a32793..6f09597 100644 --- a/blockdev.h +++ b/blockdev.h @@ -71,5 +71,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/hmp-commands.hx b/hmp-commands.hx index 74a74d8..2470c3f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -67,6 +67,20 @@ Stop an active block streaming operation. ETEXI { + .name = "block_job_set_speed", + .args_type = "device:B,value:o", + .params = "device value", + .help = "Set the maximum speed for a background block operation", + .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 + + { .name = "q|quit", .args_type = "", .params = "", diff --git a/qmp-commands.hx b/qmp-commands.hx index c3a72ad..c969909 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1051,6 +1051,41 @@ Examples: EQMP { + .name = "block_job_set_speed", + .args_type = "device:B,value:o", + .params = "device value", + .help = "Set maximum speed for a background block operation", + .mhandler.cmd_new = do_block_job_set_speed, + }, + +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 = "qmp_capabilities", .args_type = "", .params = "",