From patchwork Wed Nov 2 06:01:42 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhi Yong Wu X-Patchwork-Id: 123224 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 CE3ADB6F70 for ; Wed, 2 Nov 2011 17:05:31 +1100 (EST) Received: from localhost ([::1]:41493 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RLTx2-0007xd-8c for incoming@patchwork.ozlabs.org; Wed, 02 Nov 2011 02:05:28 -0400 Received: from eggs.gnu.org ([140.186.70.92]:47932) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RLTwl-0007rl-TS for qemu-devel@nongnu.org; Wed, 02 Nov 2011 02:05:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RLTwk-0001R7-1y for qemu-devel@nongnu.org; Wed, 02 Nov 2011 02:05:11 -0400 Received: from e3.ny.us.ibm.com ([32.97.182.143]:44558) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RLTwj-0001Qy-Ua for qemu-devel@nongnu.org; Wed, 02 Nov 2011 02:05:10 -0400 Received: from /spool/local by e3.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 2 Nov 2011 02:05:08 -0400 Received: from d01relay07.pok.ibm.com ([9.56.227.147]) by e3.ny.us.ibm.com ([192.168.1.103]) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 2 Nov 2011 02:05:00 -0400 Received: from d01av02.pok.ibm.com (d01av02.pok.ibm.com [9.56.224.216]) by d01relay07.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id pA264rS92646244 for ; Wed, 2 Nov 2011 02:04:53 -0400 Received: from d01av02.pok.ibm.com (loopback [127.0.0.1]) by d01av02.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id pA264qPt002790 for ; Wed, 2 Nov 2011 04:04:52 -0200 Received: from us.ibm.com ([9.115.118.38]) by d01av02.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with SMTP id pA264lPg030610; Wed, 2 Nov 2011 04:04:48 -0200 Received: by us.ibm.com (sSMTP sendmail emulation); Wed, 2 Nov 2011 14:03:49 +0800 From: Zhi Yong Wu To: kwolf@redhat.com Date: Wed, 2 Nov 2011 14:01:42 +0800 Message-Id: <1320213705-8097-3-git-send-email-wuzhy@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.6 In-Reply-To: <1320213705-8097-1-git-send-email-wuzhy@linux.vnet.ibm.com> References: <1320213705-8097-1-git-send-email-wuzhy@linux.vnet.ibm.com> x-cbid: 11110206-8974-0000-0000-00000156114E X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 32.97.182.143 Cc: zwu.kernel@gmail.com, ryanh@us.ibm.com, Zhi Yong Wu , qemu-devel@nongnu.org, stefanha@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH v11 2/4] block: add I/O throttling algorithm 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 Signed-off-by: Zhi Yong Wu Signed-off-by: Stefan Hajnoczi --- block.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++-- block.h | 1 + block_int.h | 1 + qemu-coroutine-lock.c | 8 ++ qemu-coroutine.h | 6 ++ 5 files changed, 242 insertions(+), 7 deletions(-) diff --git a/block.c b/block.c index c70f86d..440e889 100644 --- a/block.c +++ b/block.c @@ -74,6 +74,13 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, bool is_write); static void coroutine_fn bdrv_co_do_rw(void *opaque); +static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, + bool is_write, double elapsed_time, uint64_t *wait); +static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write, + double elapsed_time, uint64_t *wait); +static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors, + bool is_write, int64_t *wait); + static QTAILQ_HEAD(, BlockDriverState) bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states); @@ -107,6 +114,28 @@ int is_windows_drive(const char *filename) #endif /* throttling disk I/O limits */ +void bdrv_io_limits_disable(BlockDriverState *bs) +{ + bs->io_limits_enabled = false; + + if (!qemu_co_queue_empty(&bs->throttled_reqs)) { + while (qemu_co_queue_next(&bs->throttled_reqs)); + } + + qemu_co_queue_init(&bs->throttled_reqs); + + if (bs->block_timer) { + qemu_del_timer(bs->block_timer); + qemu_free_timer(bs->block_timer); + bs->block_timer = NULL; + } + + bs->slice_start = 0; + bs->slice_end = 0; + bs->slice_time = 0; + memset(&bs->io_disps, 0, sizeof(bs->io_disps)); +} + static void bdrv_block_timer(void *opaque) { BlockDriverState *bs = opaque; @@ -116,14 +145,13 @@ static void bdrv_block_timer(void *opaque) void bdrv_io_limits_enable(BlockDriverState *bs) { - bs->io_limits_enabled = true; qemu_co_queue_init(&bs->throttled_reqs); - - bs->block_timer = qemu_new_timer_ns(vm_clock, bdrv_block_timer, bs); - bs->slice_time = 5 * BLOCK_IO_SLICE_TIME; - bs->slice_start = qemu_get_clock_ns(vm_clock); - bs->slice_end = bs->slice_start + bs->slice_time; + bs->block_timer = qemu_new_timer_ns(vm_clock, bdrv_block_timer, bs); + bs->slice_time = 5 * BLOCK_IO_SLICE_TIME; + bs->slice_start = qemu_get_clock_ns(vm_clock); + bs->slice_end = bs->slice_start + bs->slice_time; memset(&bs->io_disps, 0, sizeof(bs->io_disps)); + bs->io_limits_enabled = true; } bool bdrv_io_limits_enabled(BlockDriverState *bs) @@ -137,6 +165,30 @@ bool bdrv_io_limits_enabled(BlockDriverState *bs) || io_limits->iops[BLOCK_IO_LIMIT_TOTAL]; } +static void bdrv_io_limits_intercept(BlockDriverState *bs, + bool is_write, int nb_sectors) +{ + int64_t wait_time = -1; + + if (!qemu_co_queue_empty(&bs->throttled_reqs)) { + qemu_co_queue_wait(&bs->throttled_reqs); + goto resume; + } else if (bdrv_exceed_io_limits(bs, nb_sectors, is_write, &wait_time)) { + qemu_mod_timer(bs->block_timer, + wait_time + qemu_get_clock_ns(vm_clock)); + qemu_co_queue_wait(&bs->throttled_reqs); + +resume: + while (bdrv_exceed_io_limits(bs, nb_sectors, is_write, &wait_time)) { + qemu_mod_timer(bs->block_timer, + wait_time + qemu_get_clock_ns(vm_clock)); + qemu_co_queue_wait_insert_head(&bs->throttled_reqs); + } + + qemu_co_queue_next(&bs->throttled_reqs); + } +} + /* check if the path starts with ":" */ static int path_has_protocol(const char *path) { @@ -719,6 +771,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, bdrv_dev_change_media_cb(bs, true); } + /* throttling disk I/O limits */ + if (bs->io_limits_enabled) { + bdrv_io_limits_enable(bs); + } + return 0; unlink_and_fail: @@ -754,6 +811,9 @@ void bdrv_close(BlockDriverState *bs) bdrv_dev_change_media_cb(bs, false); } + + /*throttling disk I/O limits*/ + bdrv_io_limits_disable(bs); } void bdrv_close_all(void) @@ -1292,6 +1352,11 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, return -EIO; } + /* throttling disk read I/O */ + if (bs->io_limits_enabled) { + bdrv_io_limits_intercept(bs, false, nb_sectors); + } + return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); } @@ -1322,6 +1387,11 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, return -EIO; } + /* throttling disk write I/O */ + if (bs->io_limits_enabled) { + bdrv_io_limits_intercept(bs, true, nb_sectors); + } + ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); if (bs->dirty_bitmap) { @@ -2048,7 +2118,6 @@ void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event) } return drv->bdrv_debug_event(bs, event); - } /**************************************************************/ @@ -2513,6 +2582,156 @@ void bdrv_aio_cancel(BlockDriverAIOCB *acb) acb->pool->cancel(acb); } +/* block I/O throttling */ +static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, + bool is_write, double elapsed_time, uint64_t *wait) { + uint64_t bps_limit = 0; + double bytes_limit, bytes_disp, bytes_res; + double slice_time, wait_time; + + if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) { + bps_limit = bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]; + } else if (bs->io_limits.bps[is_write]) { + bps_limit = bs->io_limits.bps[is_write]; + } else { + if (wait) { + *wait = 0; + } + + return false; + } + + slice_time = bs->slice_end - bs->slice_start; + slice_time /= (NANOSECONDS_PER_SECOND); + bytes_limit = bps_limit * slice_time; + bytes_disp = bs->nr_bytes[is_write] - bs->io_disps.bytes[is_write]; + if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) { + bytes_disp += bs->nr_bytes[!is_write] - bs->io_disps.bytes[!is_write]; + } + + bytes_res = (unsigned) nb_sectors * BDRV_SECTOR_SIZE; + + if (bytes_disp + bytes_res <= bytes_limit) { + if (wait) { + *wait = 0; + } + + return false; + } + + /* Calc approx time to dispatch */ + wait_time = (bytes_disp + bytes_res) / bps_limit - elapsed_time; + + bs->slice_time = wait_time * BLOCK_IO_SLICE_TIME * 10; + bs->slice_end += bs->slice_time - 3 * BLOCK_IO_SLICE_TIME; + if (wait) { + *wait = wait_time * BLOCK_IO_SLICE_TIME * 10; + } + + return true; +} + +static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write, + double elapsed_time, uint64_t *wait) { + uint64_t iops_limit = 0; + double ios_limit, ios_disp; + double slice_time, wait_time; + + if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) { + iops_limit = bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]; + } else if (bs->io_limits.iops[is_write]) { + iops_limit = bs->io_limits.iops[is_write]; + } else { + if (wait) { + *wait = 0; + } + + return false; + } + + slice_time = bs->slice_end - bs->slice_start; + slice_time /= (NANOSECONDS_PER_SECOND); + ios_limit = iops_limit * slice_time; + ios_disp = bs->nr_ops[is_write] - bs->io_disps.ios[is_write]; + if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) { + ios_disp += bs->nr_ops[!is_write] - bs->io_disps.ios[!is_write]; + } + + if (ios_disp + 1 <= ios_limit) { + if (wait) { + *wait = 0; + } + + return false; + } + + /* Calc approx time to dispatch */ + wait_time = (ios_disp + 1) / iops_limit; + if (wait_time > elapsed_time) { + wait_time = wait_time - elapsed_time; + } else { + wait_time = 0; + } + + bs->slice_time = wait_time * BLOCK_IO_SLICE_TIME * 10; + bs->slice_end += bs->slice_time - 3 * BLOCK_IO_SLICE_TIME; + if (wait) { + *wait = wait_time * BLOCK_IO_SLICE_TIME * 10; + } + + return true; +} + +static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors, + bool is_write, int64_t *wait) { + int64_t now, max_wait; + uint64_t bps_wait = 0, iops_wait = 0; + double elapsed_time; + int bps_ret, iops_ret; + + now = qemu_get_clock_ns(vm_clock); + if ((bs->slice_start < now) + && (bs->slice_end > now)) { + bs->slice_end = now + bs->slice_time; + } else { + bs->slice_time = 5 * BLOCK_IO_SLICE_TIME; + bs->slice_start = now; + bs->slice_end = now + bs->slice_time; + + bs->io_disps.bytes[is_write] = bs->nr_bytes[is_write]; + bs->io_disps.bytes[!is_write] = bs->nr_bytes[!is_write]; + + bs->io_disps.ios[is_write] = bs->nr_ops[is_write]; + bs->io_disps.ios[!is_write] = bs->nr_ops[!is_write]; + } + + elapsed_time = now - bs->slice_start; + elapsed_time /= (NANOSECONDS_PER_SECOND); + + bps_ret = bdrv_exceed_bps_limits(bs, nb_sectors, + is_write, elapsed_time, &bps_wait); + iops_ret = bdrv_exceed_iops_limits(bs, is_write, + elapsed_time, &iops_wait); + if (bps_ret || iops_ret) { + max_wait = bps_wait > iops_wait ? bps_wait : iops_wait; + if (wait) { + *wait = max_wait; + } + + now = qemu_get_clock_ns(vm_clock); + if (bs->slice_end < now + max_wait) { + bs->slice_end = now + max_wait; + } + + return true; + } + + if (wait) { + *wait = 0; + } + + return false; +} /**************************************************************/ /* async block device emulation */ diff --git a/block.h b/block.h index bc8315d..9b5b35f 100644 --- a/block.h +++ b/block.h @@ -91,6 +91,7 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data); /* disk I/O throttling */ void bdrv_io_limits_enable(BlockDriverState *bs); +void bdrv_io_limits_disable(BlockDriverState *bs); bool bdrv_io_limits_enabled(BlockDriverState *bs); void bdrv_init(void); diff --git a/block_int.h b/block_int.h index 0abe843..1b1f7c1 100644 --- a/block_int.h +++ b/block_int.h @@ -39,6 +39,7 @@ #define BLOCK_IO_LIMIT_TOTAL 2 #define BLOCK_IO_SLICE_TIME 100000000 +#define NANOSECONDS_PER_SECOND 1000000000.0 #define BLOCK_OPT_SIZE "size" #define BLOCK_OPT_ENCRYPT "encryption" diff --git a/qemu-coroutine-lock.c b/qemu-coroutine-lock.c index 6b58160..9549c07 100644 --- a/qemu-coroutine-lock.c +++ b/qemu-coroutine-lock.c @@ -61,6 +61,14 @@ void coroutine_fn qemu_co_queue_wait(CoQueue *queue) assert(qemu_in_coroutine()); } +void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue) +{ + Coroutine *self = qemu_coroutine_self(); + QTAILQ_INSERT_HEAD(&queue->entries, self, co_queue_next); + qemu_coroutine_yield(); + assert(qemu_in_coroutine()); +} + bool qemu_co_queue_next(CoQueue *queue) { Coroutine *next; diff --git a/qemu-coroutine.h b/qemu-coroutine.h index b8fc4f4..8a2e5d2 100644 --- a/qemu-coroutine.h +++ b/qemu-coroutine.h @@ -118,6 +118,12 @@ void qemu_co_queue_init(CoQueue *queue); void coroutine_fn qemu_co_queue_wait(CoQueue *queue); /** + * Adds the current coroutine to the head of the CoQueue and transfers control to the + * caller of the coroutine. + */ +void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue); + +/** * Restarts the next coroutine in the CoQueue and removes it from the queue. * * Returns true if a coroutine was restarted, false if the queue is empty.