* [Qemu-devel] [RFC 1/5] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure
2014-08-12 13:42 [Qemu-devel] [RFC 0/5] Throttle group cooperative round robin scheduling Benoît Canet
@ 2014-08-12 13:42 ` Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 2/5] throttle: Add throttle group infrastructure Benoît Canet
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Benoît Canet @ 2014-08-12 13:42 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, Benoît Canet, Benoit Canet, stefanha
Group throttling will share ThrottleState between multiple bs.
As a consequence the ThrottleState will be accessed by multiple aio context.
Timers are tied to their aio context so they must go out of the ThrottleState structure.
This commit pave the way for each bs of a common ThrottleState to have it's own
timer.
Signed-off-by: Benoit Canet <benoit@irqsave.net>
---
block.c | 32 ++++++++++--------
include/block/block_int.h | 1 +
include/qemu/throttle.h | 36 +++++++++++++--------
tests/test-throttle.c | 82 ++++++++++++++++++++++++++---------------------
util/throttle.c | 73 ++++++++++++++++++++++++-----------------
5 files changed, 131 insertions(+), 93 deletions(-)
diff --git a/block.c b/block.c
index 8cf519b..d2ca851 100644
--- a/block.c
+++ b/block.c
@@ -126,7 +126,7 @@ void bdrv_set_io_limits(BlockDriverState *bs,
{
int i;
- throttle_config(&bs->throttle_state, cfg);
+ throttle_config(&bs->throttle_state, &bs->throttle_timers, cfg);
for (i = 0; i < 2; i++) {
qemu_co_enter_next(&bs->throttled_reqs[i]);
@@ -159,7 +159,7 @@ void bdrv_io_limits_disable(BlockDriverState *bs)
bdrv_start_throttled_reqs(bs);
- throttle_destroy(&bs->throttle_state);
+ throttle_timers_destroy(&bs->throttle_timers);
}
static void bdrv_throttle_read_timer_cb(void *opaque)
@@ -178,12 +178,13 @@ static void bdrv_throttle_write_timer_cb(void *opaque)
void bdrv_io_limits_enable(BlockDriverState *bs)
{
assert(!bs->io_limits_enabled);
- throttle_init(&bs->throttle_state,
- bdrv_get_aio_context(bs),
- QEMU_CLOCK_VIRTUAL,
- bdrv_throttle_read_timer_cb,
- bdrv_throttle_write_timer_cb,
- bs);
+ throttle_init(&bs->throttle_state);
+ throttle_timers_init(&bs->throttle_timers,
+ bdrv_get_aio_context(bs),
+ QEMU_CLOCK_VIRTUAL,
+ bdrv_throttle_read_timer_cb,
+ bdrv_throttle_write_timer_cb,
+ bs);
bs->io_limits_enabled = true;
}
@@ -197,7 +198,9 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
bool is_write)
{
/* does this io must wait */
- bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write);
+ bool must_wait = throttle_schedule_timer(&bs->throttle_state,
+ &bs->throttle_timers,
+ is_write);
/* if must wait or any request of this type throttled queue the IO */
if (must_wait ||
@@ -210,7 +213,8 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
/* if the next request must wait -> do nothing */
- if (throttle_schedule_timer(&bs->throttle_state, is_write)) {
+ if (throttle_schedule_timer(&bs->throttle_state, &bs->throttle_timers,
+ is_write)) {
return;
}
@@ -2031,7 +2035,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
assert(bs_new->job == NULL);
assert(bs_new->dev == NULL);
assert(bs_new->io_limits_enabled == false);
- assert(!throttle_have_timer(&bs_new->throttle_state));
+ assert(!throttle_timers_are_init(&bs_new->throttle_timers));
tmp = *bs_new;
*bs_new = *bs_old;
@@ -2049,7 +2053,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
assert(bs_new->dev == NULL);
assert(bs_new->job == NULL);
assert(bs_new->io_limits_enabled == false);
- assert(!throttle_have_timer(&bs_new->throttle_state));
+ assert(!throttle_timers_are_init(&bs_new->throttle_timers));
/* insert the nodes back into the graph node list if needed */
if (bs_new->node_name[0] != '\0') {
@@ -5663,7 +5667,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
}
if (bs->io_limits_enabled) {
- throttle_detach_aio_context(&bs->throttle_state);
+ throttle_timers_detach_aio_context(&bs->throttle_timers);
}
if (bs->drv->bdrv_detach_aio_context) {
bs->drv->bdrv_detach_aio_context(bs);
@@ -5697,7 +5701,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
bs->drv->bdrv_attach_aio_context(bs, new_context);
}
if (bs->io_limits_enabled) {
- throttle_attach_aio_context(&bs->throttle_state, new_context);
+ throttle_timers_attach_aio_context(&bs->throttle_timers, new_context);
}
}
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 7b541a0..2a0c146 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -336,6 +336,7 @@ struct BlockDriverState {
/* I/O throttling */
ThrottleState throttle_state;
+ ThrottleTimers throttle_timers;
CoQueue throttled_reqs[2];
bool io_limits_enabled;
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index b890613..b89d4d8 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -65,6 +65,9 @@ typedef struct ThrottleConfig {
typedef struct ThrottleState {
ThrottleConfig cfg; /* configuration */
int64_t previous_leak; /* timestamp of the last leak done */
+} ThrottleState;
+
+typedef struct ThrottleTimers {
QEMUTimer * timers[2]; /* timers used to do the throttling */
QEMUClockType clock_type; /* the clock used */
@@ -72,7 +75,7 @@ typedef struct ThrottleState {
QEMUTimerCB *read_timer_cb;
QEMUTimerCB *write_timer_cb;
void *timer_opaque;
-} ThrottleState;
+} ThrottleTimers;
/* operations on single leaky buckets */
void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta);
@@ -86,20 +89,23 @@ bool throttle_compute_timer(ThrottleState *ts,
int64_t *next_timestamp);
/* init/destroy cycle */
-void throttle_init(ThrottleState *ts,
- AioContext *aio_context,
- QEMUClockType clock_type,
- void (read_timer)(void *),
- void (write_timer)(void *),
- void *timer_opaque);
+void throttle_init(ThrottleState *ts);
+
+void throttle_timers_init(ThrottleTimers *tt,
+ AioContext *aio_context,
+ QEMUClockType clock_type,
+ QEMUTimerCB *read_timer_cb,
+ QEMUTimerCB *write_timer_cb,
+ void *timer_opaque);
-void throttle_destroy(ThrottleState *ts);
+void throttle_timers_destroy(ThrottleTimers *tt);
-void throttle_detach_aio_context(ThrottleState *ts);
+void throttle_timers_detach_aio_context(ThrottleTimers *tt);
-void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context);
+void throttle_timers_attach_aio_context(ThrottleTimers *tt,
+ AioContext *new_context);
-bool throttle_have_timer(ThrottleState *ts);
+bool throttle_timers_are_init(ThrottleTimers *tt);
/* configuration */
bool throttle_enabled(ThrottleConfig *cfg);
@@ -108,12 +114,16 @@ bool throttle_conflicting(ThrottleConfig *cfg);
bool throttle_is_valid(ThrottleConfig *cfg);
-void throttle_config(ThrottleState *ts, ThrottleConfig *cfg);
+void throttle_config(ThrottleState *ts,
+ ThrottleTimers *tt,
+ ThrottleConfig *cfg);
void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg);
/* usage */
-bool throttle_schedule_timer(ThrottleState *ts, bool is_write);
+bool throttle_schedule_timer(ThrottleState *ts,
+ ThrottleTimers *tt,
+ bool is_write);
void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 000ae31..66e4982 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -19,6 +19,7 @@ static AioContext *ctx;
static LeakyBucket bkt;
static ThrottleConfig cfg;
static ThrottleState ts;
+static ThrottleTimers tt;
/* useful function */
static bool double_cmp(double x, double y)
@@ -102,17 +103,19 @@ static void test_init(void)
{
int i;
- /* fill the structure with crap */
+ /* fill the structures with crap */
memset(&ts, 1, sizeof(ts));
+ memset(&tt, 1, sizeof(tt));
- /* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ /* init structures */
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* check initialized fields */
- g_assert(ts.clock_type == QEMU_CLOCK_VIRTUAL);
- g_assert(ts.timers[0]);
- g_assert(ts.timers[1]);
+ g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
+ g_assert(tt.timers[0]);
+ g_assert(tt.timers[1]);
/* check other fields where cleared */
g_assert(!ts.previous_leak);
@@ -123,17 +126,18 @@ static void test_init(void)
g_assert(!ts.cfg.buckets[i].level);
}
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static void test_destroy(void)
{
int i;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
- throttle_destroy(&ts);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
+ throttle_timers_destroy(&tt);
for (i = 0; i < 2; i++) {
- g_assert(!ts.timers[i]);
+ g_assert(!tt.timers[i]);
}
}
@@ -169,11 +173,12 @@ static void test_config_functions(void)
orig_cfg.op_size = 1;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* structure reset by throttle_init previous_leak should be null */
g_assert(!ts.previous_leak);
- throttle_config(&ts, &orig_cfg);
+ throttle_config(&ts, &tt, &orig_cfg);
/* has previous leak been initialized by throttle_config ? */
g_assert(ts.previous_leak);
@@ -181,7 +186,7 @@ static void test_config_functions(void)
/* get back the fixed configuration */
throttle_get_config(&ts, &final_cfg);
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
@@ -322,43 +327,47 @@ static void test_is_valid(void)
static void test_have_timer(void)
{
- /* zero the structure */
+ /* zero structures */
memset(&ts, 0, sizeof(ts));
+ memset(&tt, 0, sizeof(tt));
/* no timer set should return false */
- g_assert(!throttle_have_timer(&ts));
+ g_assert(!throttle_timers_are_init(&tt));
- /* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ /* init structures */
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* timer set by init should return true */
- g_assert(throttle_have_timer(&ts));
+ g_assert(throttle_timers_are_init(&tt));
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static void test_detach_attach(void)
{
- /* zero the structure */
+ /* zero structures */
memset(&ts, 0, sizeof(ts));
+ memset(&tt, 0, sizeof(tt));
/* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* timer set by init should return true */
- g_assert(throttle_have_timer(&ts));
+ g_assert(throttle_timers_are_init(&tt));
/* timer should no longer exist after detaching */
- throttle_detach_aio_context(&ts);
- g_assert(!throttle_have_timer(&ts));
+ throttle_timers_detach_aio_context(&tt);
+ g_assert(!throttle_timers_are_init(&tt));
/* timer should exist again after attaching */
- throttle_attach_aio_context(&ts, ctx);
- g_assert(throttle_have_timer(&ts));
+ throttle_timers_attach_aio_context(&tt, ctx);
+ g_assert(throttle_timers_are_init(&tt));
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
@@ -386,9 +395,10 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
cfg.op_size = op_size;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
- throttle_config(&ts, &cfg);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
+ throttle_config(&ts, &tt, &cfg);
/* account a read */
throttle_account(&ts, false, size);
@@ -413,7 +423,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
return false;
}
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
return true;
}
diff --git a/util/throttle.c b/util/throttle.c
index f976ac7..4d7c8d4 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -159,29 +159,36 @@ bool throttle_compute_timer(ThrottleState *ts,
}
/* Add timers to event loop */
-void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context)
+void throttle_timers_attach_aio_context(ThrottleTimers *tt,
+ AioContext *new_context)
{
- ts->timers[0] = aio_timer_new(new_context, ts->clock_type, SCALE_NS,
- ts->read_timer_cb, ts->timer_opaque);
- ts->timers[1] = aio_timer_new(new_context, ts->clock_type, SCALE_NS,
- ts->write_timer_cb, ts->timer_opaque);
+ tt->timers[0] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
+ tt->read_timer_cb, tt->timer_opaque);
+ tt->timers[1] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
+ tt->write_timer_cb, tt->timer_opaque);
}
/* To be called first on the ThrottleState */
-void throttle_init(ThrottleState *ts,
- AioContext *aio_context,
- QEMUClockType clock_type,
- QEMUTimerCB *read_timer_cb,
- QEMUTimerCB *write_timer_cb,
- void *timer_opaque)
+void throttle_init(ThrottleState *ts)
{
memset(ts, 0, sizeof(ThrottleState));
+}
+
+/* To be called first on the ThrottleTimers */
+void throttle_timers_init(ThrottleTimers *tt,
+ AioContext *aio_context,
+ QEMUClockType clock_type,
+ QEMUTimerCB *read_timer_cb,
+ QEMUTimerCB *write_timer_cb,
+ void *timer_opaque)
+{
+ memset(tt, 0, sizeof(ThrottleTimers));
- ts->clock_type = clock_type;
- ts->read_timer_cb = read_timer_cb;
- ts->write_timer_cb = write_timer_cb;
- ts->timer_opaque = timer_opaque;
- throttle_attach_aio_context(ts, aio_context);
+ tt->clock_type = clock_type;
+ tt->read_timer_cb = read_timer_cb;
+ tt->write_timer_cb = write_timer_cb;
+ tt->timer_opaque = timer_opaque;
+ throttle_timers_attach_aio_context(tt, aio_context);
}
/* destroy a timer */
@@ -195,25 +202,25 @@ static void throttle_timer_destroy(QEMUTimer **timer)
}
/* Remove timers from event loop */
-void throttle_detach_aio_context(ThrottleState *ts)
+void throttle_timers_detach_aio_context(ThrottleTimers *tt)
{
int i;
for (i = 0; i < 2; i++) {
- throttle_timer_destroy(&ts->timers[i]);
+ throttle_timer_destroy(&tt->timers[i]);
}
}
-/* To be called last on the ThrottleState */
-void throttle_destroy(ThrottleState *ts)
+/* To be called last on the ThrottleTimers */
+void throttle_timers_destroy(ThrottleTimers *tt)
{
- throttle_detach_aio_context(ts);
+ throttle_timers_detach_aio_context(tt);
}
/* is any throttling timer configured */
-bool throttle_have_timer(ThrottleState *ts)
+bool throttle_timers_are_init(ThrottleTimers *tt)
{
- if (ts->timers[0]) {
+ if (tt->timers[0]) {
return true;
}
@@ -324,9 +331,12 @@ static void throttle_cancel_timer(QEMUTimer *timer)
/* Used to configure the throttle
*
* @ts: the throttle state we are working on
+ * @tt: the throttle timers we use in this aio context
* @cfg: the config to set
*/
-void throttle_config(ThrottleState *ts, ThrottleConfig *cfg)
+void throttle_config(ThrottleState *ts,
+ ThrottleTimers *tt,
+ ThrottleConfig *cfg)
{
int i;
@@ -336,10 +346,10 @@ void throttle_config(ThrottleState *ts, ThrottleConfig *cfg)
throttle_fix_bucket(&ts->cfg.buckets[i]);
}
- ts->previous_leak = qemu_clock_get_ns(ts->clock_type);
+ ts->previous_leak = qemu_clock_get_ns(tt->clock_type);
for (i = 0; i < 2; i++) {
- throttle_cancel_timer(ts->timers[i]);
+ throttle_cancel_timer(tt->timers[i]);
}
}
@@ -358,12 +368,15 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg)
*
* NOTE: this function is not unit tested due to it's usage of timer_mod
*
+ * @tt: the timers structure
* @is_write: the type of operation (read/write)
* @ret: true if the timer has been scheduled else false
*/
-bool throttle_schedule_timer(ThrottleState *ts, bool is_write)
+bool throttle_schedule_timer(ThrottleState *ts,
+ ThrottleTimers *tt,
+ bool is_write)
{
- int64_t now = qemu_clock_get_ns(ts->clock_type);
+ int64_t now = qemu_clock_get_ns(tt->clock_type);
int64_t next_timestamp;
bool must_wait;
@@ -378,12 +391,12 @@ bool throttle_schedule_timer(ThrottleState *ts, bool is_write)
}
/* request throttled and timer pending -> do nothing */
- if (timer_pending(ts->timers[is_write])) {
+ if (timer_pending(tt->timers[is_write])) {
return true;
}
/* request throttled and timer not pending -> arm timer */
- timer_mod(ts->timers[is_write], next_timestamp);
+ timer_mod(tt->timers[is_write], next_timestamp);
return true;
}
--
2.1.0.rc1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [RFC 2/5] throttle: Add throttle group infrastructure.
2014-08-12 13:42 [Qemu-devel] [RFC 0/5] Throttle group cooperative round robin scheduling Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 1/5] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure Benoît Canet
@ 2014-08-12 13:42 ` Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 3/5] throttle: Add throttle group infrastructure tests Benoît Canet
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Benoît Canet @ 2014-08-12 13:42 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, Benoît Canet, Benoit Canet, stefanha
The throttle_group_incref increment the refcount of a throttle group given it's
name and return the associated throttle state.
The throttle_group_unref is the mirror function for cleaning up.
Signed-off-by: Benoit Canet <benoit@irqsave.net>
---
include/block/block_int.h | 1 +
include/qemu/throttle-groups.h | 43 +++++++++
util/Makefile.objs | 1 +
util/throttle-groups.c | 195 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 include/qemu/throttle-groups.h
create mode 100644 util/throttle-groups.c
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 2a0c146..6066f63 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -339,6 +339,7 @@ struct BlockDriverState {
ThrottleTimers throttle_timers;
CoQueue throttled_reqs[2];
bool io_limits_enabled;
+ QLIST_ENTRY(BlockDriverState) round_robin;
/* I/O stats (display with "info blockstats"). */
uint64_t nr_bytes[BDRV_MAX_IOTYPE];
diff --git a/include/qemu/throttle-groups.h b/include/qemu/throttle-groups.h
new file mode 100644
index 0000000..12c56e6
--- /dev/null
+++ b/include/qemu/throttle-groups.h
@@ -0,0 +1,43 @@
+/*
+ * QEMU throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ *
+ * Author:
+ * Benoît Canet <benoit.canet@irqsave.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef THROTTLE_GROUPS_H
+#define THROTTLE_GROUPS_H
+
+#include "qemu/throttle.h"
+#include "block/block_int.h"
+
+ThrottleState *throttle_group_incref(const char *name);
+bool throttle_group_unref(ThrottleState *ts);
+
+void throttle_group_register_bs(ThrottleState *ts, BlockDriverState *bs);
+BlockDriverState *throttle_group_next_bs(BlockDriverState *bs);
+
+void throttle_group_set_token(ThrottleState *ts,
+ BlockDriverState *token,
+ bool is_write);
+BlockDriverState *throttle_group_token(ThrottleState *ts, bool is_write);
+
+void throttle_group_lock(ThrottleState *ts);
+void throttle_group_unlock(ThrottleState *ts);
+
+#endif
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 6b3c83b..3c95397 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -12,6 +12,7 @@ util-obj-y += qemu-option.o qemu-progress.o
util-obj-y += hexdump.o
util-obj-y += crc32c.o
util-obj-y += throttle.o
+util-obj-y += throttle-groups.o
util-obj-y += getauxval.o
util-obj-y += readline.o
util-obj-y += rfifolock.o
diff --git a/util/throttle-groups.c b/util/throttle-groups.c
new file mode 100644
index 0000000..153bacc
--- /dev/null
+++ b/util/throttle-groups.c
@@ -0,0 +1,195 @@
+/*
+ * QEMU throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ *
+ * Author:
+ * Benoît Canet <benoit.canet@irqsave.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/throttle-groups.h"
+#include "qemu/queue.h"
+#include "qemu/thread.h"
+
+typedef struct ThrottleGroup {
+ char name[32];
+ ThrottleState ts;
+ uint64_t refcount;
+ QTAILQ_ENTRY(ThrottleGroup) list;
+ QLIST_HEAD(, BlockDriverState) head;
+ BlockDriverState *tokens[2]; /* current round-robin tokens */
+ QemuMutex lock; /* Used to synchronize all elements belonging to a group */
+} ThrottleGroup;
+
+static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
+ QTAILQ_HEAD_INITIALIZER(throttle_groups);
+
+/* increments a ThrottleGroup reference count given it's name
+ *
+ * If no ThrottleGroup is found with the given name a new one is created.
+ *
+ * @name: the name of the ThrottleGroup
+ * @ret: the ThrottleGroup's ThrottleState address
+ */
+ThrottleState *throttle_group_incref(const char *name)
+{
+ ThrottleGroup *tg;
+
+ /* return the correct ThrottleState if a group with this name exists */
+ QTAILQ_FOREACH(tg, &throttle_groups, list) {
+ /* group not found -> continue */
+ if (strcmp(name, tg->name)) {
+ continue;
+ }
+ /* group found -> increment it's refcount and return ThrottleState */
+ tg->refcount++;
+ return &tg->ts;
+ }
+
+ /* throttle group not found -> prepare new entry */
+ tg = g_new0(ThrottleGroup, 1);
+ throttle_init(&tg->ts);
+ pstrcpy(tg->name, sizeof(tg->name), name);
+ tg->refcount = 1;
+ qemu_mutex_init(&tg->lock);
+ QLIST_INIT(&tg->head);
+
+ /* insert new entry in the list */
+ QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+
+ /* return newly allocated ThrottleState */
+ return &tg->ts;
+}
+
+/* decrement a ThrottleGroup given it's ThrottleState address
+ *
+ * When the refcount reach zero the ThrottleGroup is destroyed
+ *
+ * @ts: The ThrottleState address belonging to the ThrottleGroup to unref
+ * @ret: true on success else false
+ */
+bool throttle_group_unref(ThrottleState *ts)
+{
+ ThrottleGroup *tg;
+ bool found = false;
+
+ /* Find the ThrottleGroup of the given ThrottleState */
+ QTAILQ_FOREACH(tg, &throttle_groups, list) {
+ /* correct group found stop iterating */
+ if (&tg->ts == ts) {
+ qemu_mutex_lock(&tg->lock);
+ found = true;
+ break;
+ }
+ }
+
+ /* If the ThrottleState was not found something is seriously broken */
+ if (!found) {
+ return false;
+ }
+
+ tg->refcount--;
+
+ /* If ThrottleGroup is used keep it. */
+ if (tg->refcount) {
+ qemu_mutex_unlock(&tg->lock);
+ return true;
+ }
+
+ /* Else destroy it */
+ QTAILQ_REMOVE(&throttle_groups, tg, list);
+ qemu_mutex_unlock(&tg->lock);
+ qemu_mutex_destroy(&tg->lock);
+ g_free(tg);
+ return true;
+}
+
+/* Register a BlockDriverState in the doubly linked list
+ *
+ * @ts: the ThrottleState the code is working on
+ * @bs: the BlockDriverState to insert
+ */
+void throttle_group_register_bs(ThrottleState *ts, BlockDriverState *bs)
+{
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ QLIST_INSERT_HEAD(&tg->head, bs, round_robin);
+}
+
+/* Return the next BlockDriverState in the round-robin sequence
+ *
+ * This takes care of simulating a circular list
+ *
+ * @bs: the input current BlockDriverState
+ * @ret: the next BlockDriverState in the sequence
+ */
+BlockDriverState *throttle_group_next_bs(BlockDriverState *bs)
+{
+ ThrottleState *ts = &bs->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ BlockDriverState *next = QLIST_NEXT(bs, round_robin);
+
+ if (!next) {
+ return QLIST_FIRST(&tg->head);
+ }
+
+ return next;
+}
+
+/* Used to set a token BlockDriverState into a ThrottleState's ThrottleGroup
+ *
+ * @ts: the ThrottleState the code is working on
+ * @token: the token BlockDriverState to set
+ * @is_write: true if we are talking about write operations else false
+ */
+void throttle_group_set_token(ThrottleState *ts,
+ BlockDriverState *token,
+ bool is_write)
+{
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ tg->tokens[is_write] = token;
+}
+
+/* Used to get the token BlockDriverState of a ThrottleState's ThrottleGroup
+ *
+ * @ts: the ThrottleState the code is working on
+ * @ret: the token BlockDriverState that is retrieved
+ * @is_write: true if we are talking about write operations else false
+ */
+BlockDriverState *throttle_group_token(ThrottleState *ts, bool is_write)
+{
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ return tg->tokens[is_write];
+}
+
+/* Used to lock a ThrottleState's ThrottleGroup
+ *
+ * @ts: the ThrottleState the code is working on
+ */
+void throttle_group_lock(ThrottleState *ts)
+{
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ qemu_mutex_lock(&tg->lock);
+}
+
+/* Used to unlock a ThrottleState's ThrottleGroup
+ *
+ * @ts: the ThrottleState the code is working on
+ */
+void throttle_group_unlock(ThrottleState *ts)
+{
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ qemu_mutex_unlock(&tg->lock);
+}
--
2.1.0.rc1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [RFC 3/5] throttle: Add throttle group infrastructure tests
2014-08-12 13:42 [Qemu-devel] [RFC 0/5] Throttle group cooperative round robin scheduling Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 1/5] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 2/5] throttle: Add throttle group infrastructure Benoît Canet
@ 2014-08-12 13:42 ` Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 4/5] throttle: Prepare to have multiple timers for one ThrottleState Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 5/5] throttle: Add throttle group support Benoît Canet
4 siblings, 0 replies; 6+ messages in thread
From: Benoît Canet @ 2014-08-12 13:42 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, Benoît Canet, Benoit Canet, stefanha
Signed-off-by: Benoit Canet <benoit@irqsave.net>
---
tests/test-throttle.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 66e4982..494a806 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -14,6 +14,7 @@
#include <math.h>
#include "block/aio.h"
#include "qemu/throttle.h"
+#include "qemu/throttle-groups.h"
static AioContext *ctx;
static LeakyBucket bkt;
@@ -499,6 +500,55 @@ static void test_accounting(void)
(64.0 / 13)));
}
+static void test_groups(void)
+{
+ bool removed;
+
+ ThrottleState *ts_foo, *ts_bar, *tmp;
+
+ ts_bar = throttle_group_incref("bar");
+ throttle_group_set_token(ts_bar, (BlockDriverState *) 0x5, false);
+ ts_foo = throttle_group_incref("foo");
+
+ tmp = throttle_group_incref("foo");
+ throttle_group_set_token(tmp, (BlockDriverState *) 0x7, true);
+ g_assert(tmp == ts_foo);
+
+ tmp = throttle_group_incref("bar");
+ g_assert(tmp == ts_bar);
+
+ tmp = throttle_group_incref("bar");
+ g_assert(tmp == ts_bar);
+
+ g_assert((int64_t) throttle_group_token(ts_bar, false) == 0x5);
+ g_assert((int64_t) throttle_group_token(ts_foo, true) == 0x7);
+
+ removed = throttle_group_unref(ts_foo);
+ g_assert(removed);
+ removed = throttle_group_unref(ts_bar);
+ g_assert(removed);
+
+ g_assert((int64_t) throttle_group_token(ts_foo, true) == 0x7);
+
+ removed = throttle_group_unref(ts_foo);
+ g_assert(removed);
+ removed = throttle_group_unref(ts_bar);
+ g_assert(removed);
+
+ /* "foo" group should be destroyed when reaching this */
+ removed = throttle_group_unref(ts_foo);
+ g_assert(!removed);
+
+ g_assert((int64_t) throttle_group_token(ts_bar, false) == 0x5);
+
+ removed = throttle_group_unref(ts_bar);
+ g_assert(removed);
+
+ /* "bar" group should be destroyed when reaching this */
+ removed = throttle_group_unref(ts_bar);
+ g_assert(!removed);
+}
+
int main(int argc, char **argv)
{
GSource *src;
@@ -525,6 +575,7 @@ int main(int argc, char **argv)
g_test_add_func("/throttle/config/is_valid", test_is_valid);
g_test_add_func("/throttle/config_functions", test_config_functions);
g_test_add_func("/throttle/accounting", test_accounting);
+ g_test_add_func("/throttle/groups", test_groups);
return g_test_run();
}
--
2.1.0.rc1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [RFC 4/5] throttle: Prepare to have multiple timers for one ThrottleState
2014-08-12 13:42 [Qemu-devel] [RFC 0/5] Throttle group cooperative round robin scheduling Benoît Canet
` (2 preceding siblings ...)
2014-08-12 13:42 ` [Qemu-devel] [RFC 3/5] throttle: Add throttle group infrastructure tests Benoît Canet
@ 2014-08-12 13:42 ` Benoît Canet
2014-08-12 13:42 ` [Qemu-devel] [RFC 5/5] throttle: Add throttle group support Benoît Canet
4 siblings, 0 replies; 6+ messages in thread
From: Benoît Canet @ 2014-08-12 13:42 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, Benoît Canet, Benoit Canet, stefanha
This patch transform the timer_pending call into two boolean values in the
ThrottleState structure.
This way we are sure that when multiple timers will be used only
one can be armed at a time.
Signed-off-by: Benoit Canet <benoit@irqsave.net>
---
block.c | 2 ++
include/qemu/throttle.h | 3 +++
util/throttle.c | 17 ++++++++++++++---
3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/block.c b/block.c
index d2ca851..5edb3c3 100644
--- a/block.c
+++ b/block.c
@@ -165,12 +165,14 @@ void bdrv_io_limits_disable(BlockDriverState *bs)
static void bdrv_throttle_read_timer_cb(void *opaque)
{
BlockDriverState *bs = opaque;
+ throttle_timer_fired(&bs->throttle_state, false);
qemu_co_enter_next(&bs->throttled_reqs[0]);
}
static void bdrv_throttle_write_timer_cb(void *opaque)
{
BlockDriverState *bs = opaque;
+ throttle_timer_fired(&bs->throttle_state, true);
qemu_co_enter_next(&bs->throttled_reqs[1]);
}
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index b89d4d8..3aece3a 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -65,6 +65,7 @@ typedef struct ThrottleConfig {
typedef struct ThrottleState {
ThrottleConfig cfg; /* configuration */
int64_t previous_leak; /* timestamp of the last leak done */
+ bool any_timer_armed[2]; /* is any timer armed for this throttle state */
} ThrottleState;
typedef struct ThrottleTimers {
@@ -125,6 +126,8 @@ bool throttle_schedule_timer(ThrottleState *ts,
ThrottleTimers *tt,
bool is_write);
+void throttle_timer_fired(ThrottleState *ts, bool is_write);
+
void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
#endif
diff --git a/util/throttle.c b/util/throttle.c
index 4d7c8d4..0e305e3 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -172,6 +172,7 @@ void throttle_timers_attach_aio_context(ThrottleTimers *tt,
void throttle_init(ThrottleState *ts)
{
memset(ts, 0, sizeof(ThrottleState));
+ ts->any_timer_armed[0] = ts->any_timer_armed[1] = false;
}
/* To be called first on the ThrottleTimers */
@@ -390,16 +391,26 @@ bool throttle_schedule_timer(ThrottleState *ts,
return false;
}
- /* request throttled and timer pending -> do nothing */
- if (timer_pending(tt->timers[is_write])) {
+ /* request throttled and any timer pending -> do nothing */
+ if (ts->any_timer_armed[is_write]) {
return true;
}
- /* request throttled and timer not pending -> arm timer */
+ ts->any_timer_armed[is_write] = true;
timer_mod(tt->timers[is_write], next_timestamp);
return true;
}
+/* Remember that now timers are currently armed
+ *
+ * @ts: the throttle state we are working on
+ * @is_write: the type of operation (read/write)
+ */
+void throttle_timer_fired(ThrottleState *ts, bool is_write)
+{
+ ts->any_timer_armed[is_write] = false;
+}
+
/* do the accounting for this operation
*
* @is_write: the type of operation (read/write)
--
2.1.0.rc1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [RFC 5/5] throttle: Add throttle group support.
2014-08-12 13:42 [Qemu-devel] [RFC 0/5] Throttle group cooperative round robin scheduling Benoît Canet
` (3 preceding siblings ...)
2014-08-12 13:42 ` [Qemu-devel] [RFC 4/5] throttle: Prepare to have multiple timers for one ThrottleState Benoît Canet
@ 2014-08-12 13:42 ` Benoît Canet
4 siblings, 0 replies; 6+ messages in thread
From: Benoît Canet @ 2014-08-12 13:42 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, Benoît Canet, Benoit Canet, stefanha
Signed-off-by: Benoit Canet <benoit@irqsave.net>
---
block.c | 157 ++++++++++++++++++++++++++++++++++++++++------
block/qapi.c | 2 +-
blockdev.c | 16 ++++-
hmp.c | 4 +-
include/block/block.h | 2 +-
include/block/block_int.h | 9 +--
include/qemu/throttle.h | 5 +-
qapi/block-core.json | 5 +-
qemu-options.hx | 1 +
qmp-commands.hx | 3 +-
util/throttle-groups.c | 2 +-
util/throttle.c | 23 +++++--
12 files changed, 192 insertions(+), 37 deletions(-)
diff --git a/block.c b/block.c
index 5edb3c3..92f9171 100644
--- a/block.c
+++ b/block.c
@@ -35,6 +35,7 @@
#include "qmp-commands.h"
#include "qemu/timer.h"
#include "qapi-event.h"
+#include "qemu/throttle-groups.h"
#ifdef CONFIG_BSD
#include <sys/types.h>
@@ -126,7 +127,9 @@ void bdrv_set_io_limits(BlockDriverState *bs,
{
int i;
- throttle_config(&bs->throttle_state, &bs->throttle_timers, cfg);
+ throttle_group_lock(bs->throttle_state);
+ throttle_config(bs->throttle_state, &bs->throttle_timers, cfg);
+ throttle_group_unlock(bs->throttle_state);
for (i = 0; i < 2; i++) {
qemu_co_enter_next(&bs->throttled_reqs[i]);
@@ -153,34 +156,93 @@ static bool bdrv_start_throttled_reqs(BlockDriverState *bs)
return drained;
}
+static void bdrv_throttle_group_add(BlockDriverState *bs)
+{
+ int i;
+ BlockDriverState *token;
+
+ for (i = 0; i < 2; i++) {
+ /* Get the BlockDriverState having the round robin token */
+ token = throttle_group_token(bs->throttle_state, i);
+
+ /* If the ThrottleGroup is new set the current BlockDriverState as
+ * token
+ */
+ if (!token) {
+ throttle_group_set_token(bs->throttle_state, bs, i);
+ }
+
+ }
+
+ throttle_group_register_bs(bs->throttle_state, bs);
+}
+
+static void bdrv_throttle_group_remove(BlockDriverState *bs)
+{
+ BlockDriverState *token;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ /* Get the BlockDriverState having the round robin token */
+ token = throttle_group_token(bs->throttle_state, i);
+ /* if this bs is the current token set the next bs as token */
+ if (token == bs) {
+ token = throttle_group_next_bs(token);
+ throttle_group_set_token(bs->throttle_state, token, i);
+ }
+ }
+
+ /* remove the current bs from the list */
+ QLIST_REMOVE(bs, round_robin);
+}
+
void bdrv_io_limits_disable(BlockDriverState *bs)
{
- bs->io_limits_enabled = false;
+ throttle_group_lock(bs->throttle_state);
+
+ bs->io_limits_enabled = false;
bdrv_start_throttled_reqs(bs);
+ bdrv_throttle_group_remove(bs);
+
+ /* unlock all throttle group related elements */
+ throttle_group_unlock(bs->throttle_state);
+
+ throttle_group_unref(bs->throttle_state);
+ bs->throttle_state = NULL;
+
throttle_timers_destroy(&bs->throttle_timers);
}
static void bdrv_throttle_read_timer_cb(void *opaque)
{
BlockDriverState *bs = opaque;
- throttle_timer_fired(&bs->throttle_state, false);
+ throttle_group_lock(bs->throttle_state);
+ throttle_timer_fired(bs->throttle_state, false);
+ throttle_group_unlock(bs->throttle_state);
qemu_co_enter_next(&bs->throttled_reqs[0]);
}
static void bdrv_throttle_write_timer_cb(void *opaque)
{
BlockDriverState *bs = opaque;
- throttle_timer_fired(&bs->throttle_state, true);
+ throttle_group_lock(bs->throttle_state);
+ throttle_timer_fired(bs->throttle_state, true);
+ throttle_group_unlock(bs->throttle_state);
qemu_co_enter_next(&bs->throttled_reqs[1]);
}
/* should be called before bdrv_set_io_limits if a limit is set */
-void bdrv_io_limits_enable(BlockDriverState *bs)
+void bdrv_io_limits_enable(BlockDriverState *bs, const char *group)
{
assert(!bs->io_limits_enabled);
- throttle_init(&bs->throttle_state);
+ bs->throttle_state = throttle_group_incref(group ? group: bs->device_name);
+
+ throttle_group_lock(bs->throttle_state);
+
+ bdrv_throttle_group_add(bs);
+
throttle_timers_init(&bs->throttle_timers,
bdrv_get_aio_context(bs),
QEMU_CLOCK_VIRTUAL,
@@ -188,6 +250,33 @@ void bdrv_io_limits_enable(BlockDriverState *bs)
bdrv_throttle_write_timer_cb,
bs);
bs->io_limits_enabled = true;
+
+ /* unlock all throttle group related elements */
+ throttle_group_unlock(bs->throttle_state);
+}
+
+/* This implement the round robin policy and must be called under ThrottleGroup
+ * lock
+ */
+static BlockDriverState *bdrv_next_throttle_token(BlockDriverState *bs,
+ bool is_write)
+{
+ BlockDriverState *token, *start;
+
+ start = token = throttle_group_token(bs->throttle_state, is_write);
+
+ token = throttle_group_next_bs(token);
+ while (token != start &&
+ qemu_co_queue_empty(&token->throttled_reqs[is_write])) {
+ token = throttle_group_next_bs(token);
+ }
+
+ if (token == start &&
+ qemu_co_queue_empty(&token->throttled_reqs[is_write])) {
+ token = bs;
+ }
+
+ return token;
}
/* This function makes an IO wait if needed
@@ -199,29 +288,63 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
unsigned int bytes,
bool is_write)
{
+ bool empty;
+ bool armed;
+ bool token_queue_empty;
+ BlockDriverState *token;
+
+ throttle_group_lock(bs->throttle_state);
+ token = bdrv_next_throttle_token(bs, is_write);
+
/* does this io must wait */
- bool must_wait = throttle_schedule_timer(&bs->throttle_state,
- &bs->throttle_timers,
- is_write);
+ bool must_wait = throttle_schedule_timer(bs->throttle_state,
+ &token->throttle_timers,
+ is_write,
+ &armed);
+
+ empty = qemu_co_queue_empty(&bs->throttled_reqs[is_write]);
+
+ if (armed) {
+ throttle_group_set_token(bs->throttle_state, token, is_write);
+ }
+
+ throttle_group_unlock(bs->throttle_state);
/* if must wait or any request of this type throttled queue the IO */
- if (must_wait ||
- !qemu_co_queue_empty(&bs->throttled_reqs[is_write])) {
+ if (must_wait || !empty) {
qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
}
+ throttle_group_lock(bs->throttle_state);
+ token = bdrv_next_throttle_token(bs, is_write);
+ token_queue_empty = qemu_co_queue_empty(&token->throttled_reqs[is_write]);
+
+
/* the IO will be executed, do the accounting */
- throttle_account(&bs->throttle_state, is_write, bytes);
+ throttle_account(bs->throttle_state, is_write, bytes);
+ must_wait = throttle_schedule_timer(bs->throttle_state,
+ &token->throttle_timers,
+ is_write,
+ &armed);
+
+ if (armed || !token_queue_empty) {
+ throttle_group_set_token(bs->throttle_state, token, is_write);
+ }
/* if the next request must wait -> do nothing */
- if (throttle_schedule_timer(&bs->throttle_state, &bs->throttle_timers,
- is_write)) {
+ if (must_wait) {
+ throttle_group_unlock(bs->throttle_state);
return;
}
/* else queue next request for execution */
- qemu_co_queue_next(&bs->throttled_reqs[is_write]);
+ if (!qemu_co_queue_empty(&bs->throttled_reqs[is_write])) {
+ qemu_co_queue_next(&bs->throttled_reqs[is_write]);
+ } else if(!token_queue_empty) {
+ throttle_arm_timer(&token->throttle_timers, is_write);
+ }
+ throttle_group_unlock(bs->throttle_state);
}
size_t bdrv_opt_mem_align(BlockDriverState *bs)
@@ -1972,9 +2095,7 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
bs_dest->enable_write_cache = bs_src->enable_write_cache;
/* i/o throttled req */
- memcpy(&bs_dest->throttle_state,
- &bs_src->throttle_state,
- sizeof(ThrottleState));
+ bs_dest->throttle_state = bs_src->throttle_state,
bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0];
bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1];
bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
diff --git a/block/qapi.c b/block/qapi.c
index f44f6b4..9ea11a0 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -54,7 +54,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
if (bs->io_limits_enabled) {
ThrottleConfig cfg;
- throttle_get_config(&bs->throttle_state, &cfg);
+ throttle_get_config(bs->throttle_state, &cfg);
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
diff --git a/blockdev.c b/blockdev.c
index 48bd9a3..67e2d29 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -330,6 +330,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
bool has_driver_specific_opts;
BlockdevDetectZeroesOptions detect_zeroes;
BlockDriver *drv = NULL;
+ const char *throttling_group;
/* Check common options by copying from bs_opts to opts, all other options
* stay in bs_opts for processing by bdrv_open(). */
@@ -432,6 +433,8 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
+ throttling_group = qemu_opt_get(opts, "throttling.group");
+
if (!check_throttle_config(&cfg, &error)) {
error_propagate(errp, error);
goto early_err;
@@ -490,7 +493,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
/* disk I/O throttling */
if (throttle_enabled(&cfg)) {
- bdrv_io_limits_enable(dinfo->bdrv);
+ bdrv_io_limits_enable(dinfo->bdrv, throttling_group);
bdrv_set_io_limits(dinfo->bdrv, &cfg);
}
@@ -679,6 +682,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
qemu_opt_rename(all_opts,
"iops_size", "throttling.iops-size");
+ qemu_opt_rename(all_opts, "group", "throttling.group");
qemu_opt_rename(all_opts, "readonly", "read-only");
@@ -1689,7 +1693,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
bool has_iops_wr_max,
int64_t iops_wr_max,
bool has_iops_size,
- int64_t iops_size, Error **errp)
+ int64_t iops_size,
+ bool has_group,
+ const char *group, Error **errp)
{
ThrottleConfig cfg;
BlockDriverState *bs;
@@ -1741,7 +1747,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
aio_context_acquire(aio_context);
if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
- bdrv_io_limits_enable(bs);
+ bdrv_io_limits_enable(bs, has_group ? group : NULL);
} else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
bdrv_io_limits_disable(bs);
}
@@ -2643,6 +2649,10 @@ QemuOptsList qemu_common_drive_opts = {
.type = QEMU_OPT_NUMBER,
.help = "when limiting by iops max size of an I/O in bytes",
},{
+ .name = "throttling.group",
+ .type = QEMU_OPT_STRING,
+ .help = "name of the block throttling group",
+ },{
.name = "copy-on-read",
.type = QEMU_OPT_BOOL,
.help = "copy read data from backing file into image file",
diff --git a/hmp.c b/hmp.c
index 4d1838e..c580b0e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1165,7 +1165,9 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
false,
0,
false, /* No default I/O size */
- 0, &err);
+ 0,
+ false,
+ NULL, &err);
hmp_handle_error(mon, &err);
}
diff --git a/include/block/block.h b/include/block/block.h
index f08471d..761006d 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -190,7 +190,7 @@ void bdrv_stats_print(Monitor *mon, const QObject *data);
void bdrv_info_stats(Monitor *mon, QObject **ret_data);
/* disk I/O throttling */
-void bdrv_io_limits_enable(BlockDriverState *bs);
+void bdrv_io_limits_enable(BlockDriverState *bs, const char *group);
void bdrv_io_limits_disable(BlockDriverState *bs);
void bdrv_init(void);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 6066f63..fbf5d2e 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -334,12 +334,13 @@ struct BlockDriverState {
/* number of in-flight serialising requests */
unsigned int serialising_in_flight;
- /* I/O throttling */
- ThrottleState throttle_state;
- ThrottleTimers throttle_timers;
- CoQueue throttled_reqs[2];
+ /* I/O throttling - following elements protected by ThrottleGroup lock */
+ ThrottleState *throttle_state;
bool io_limits_enabled;
+ CoQueue throttled_reqs[2];
QLIST_ENTRY(BlockDriverState) round_robin;
+ /* timers have their own locking */
+ ThrottleTimers throttle_timers;
/* I/O stats (display with "info blockstats"). */
uint64_t nr_bytes[BDRV_MAX_IOTYPE];
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 3aece3a..5f5dde2 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -124,7 +124,10 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg);
/* usage */
bool throttle_schedule_timer(ThrottleState *ts,
ThrottleTimers *tt,
- bool is_write);
+ bool is_write,
+ bool *armed);
+
+void throttle_arm_timer(ThrottleTimers *tt, bool is_write);
void throttle_timer_fired(ThrottleState *ts, bool is_write);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index e378653..aa307a2 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -886,6 +886,9 @@
#
# @iops_size: #optional an I/O size in bytes (Since 1.7)
#
+#
+# @group: #optional throttle group name (Since 2.2)
+#
# Returns: Nothing on success
# If @device is not a valid block device, DeviceNotFound
#
@@ -897,7 +900,7 @@
'*bps_max': 'int', '*bps_rd_max': 'int',
'*bps_wr_max': 'int', '*iops_max': 'int',
'*iops_rd_max': 'int', '*iops_wr_max': 'int',
- '*iops_size': 'int' } }
+ '*iops_size': 'int', '*group': 'str' } }
##
# @block-stream:
diff --git a/qemu-options.hx b/qemu-options.hx
index 1549625..f1ca6aa 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -433,6 +433,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive,
" [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
" [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n"
" [[,iops_size=is]]\n"
+ " [[,group=g]]\n"
" use 'file' as a drive image\n", QEMU_ARCH_ALL)
STEXI
@item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 4be4765..2f25ac7 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1663,7 +1663,7 @@ EQMP
{
.name = "block_set_io_throttle",
- .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?",
+ .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?",
.mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle,
},
@@ -1689,6 +1689,7 @@ Arguments:
- "iops_rd_max": read I/O operations max (json-int)
- "iops_wr_max": write I/O operations max (json-int)
- "iops_size": I/O size in bytes when limiting (json-int)
+- "group": throttle group name (json-string)
Example:
diff --git a/util/throttle-groups.c b/util/throttle-groups.c
index 153bacc..20baca4 100644
--- a/util/throttle-groups.c
+++ b/util/throttle-groups.c
@@ -137,7 +137,7 @@ void throttle_group_register_bs(ThrottleState *ts, BlockDriverState *bs)
*/
BlockDriverState *throttle_group_next_bs(BlockDriverState *bs)
{
- ThrottleState *ts = &bs->throttle_state;
+ ThrottleState *ts = bs->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
BlockDriverState *next = QLIST_NEXT(bs, round_robin);
diff --git a/util/throttle.c b/util/throttle.c
index 0e305e3..1ee52eb 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -375,11 +375,13 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg)
*/
bool throttle_schedule_timer(ThrottleState *ts,
ThrottleTimers *tt,
- bool is_write)
+ bool is_write,
+ bool *armed)
{
int64_t now = qemu_clock_get_ns(tt->clock_type);
int64_t next_timestamp;
bool must_wait;
+ *armed = false;
must_wait = throttle_compute_timer(ts,
is_write,
@@ -392,15 +394,26 @@ bool throttle_schedule_timer(ThrottleState *ts,
}
/* request throttled and any timer pending -> do nothing */
- if (ts->any_timer_armed[is_write]) {
- return true;
+ if (!ts->any_timer_armed[is_write]) {
+ *armed = true;
+ ts->any_timer_armed[is_write] = true;
+ timer_mod(tt->timers[is_write], next_timestamp);
}
- ts->any_timer_armed[is_write] = true;
- timer_mod(tt->timers[is_write], next_timestamp);
return true;
}
+/* Schedule a throttle timer like a BH
+ *
+ * @tt: The timers structure
+ * @is_write: the type of operation (read/write)
+ */
+void throttle_arm_timer(ThrottleTimers *tt, bool is_write)
+{
+ int64_t now = qemu_clock_get_ns(tt->clock_type);
+ timer_mod(tt->timers[is_write], now + 1);
+}
+
/* Remember that now timers are currently armed
*
* @ts: the throttle state we are working on
--
2.1.0.rc1
^ permalink raw reply related [flat|nested] 6+ messages in thread