All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling
@ 2014-08-13 14:23 Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 1/8] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure Benoît Canet
                   ` (8 more replies)
  0 siblings, 9 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît Canet, stefanha

Hi,

Here is my current wip on the throttle group support.

For the user interface I implemented Stefanha's idea proposed in Stuttgart.

For the throttling algorithm I use a cooperative round robin scheduler.

Classical round robin works with a fixed HZ ticks and it's totaly incompatible
with the throttling algorithm.

So the cooperative round robin scheduler is a way for each block device to decide
if a pause must be done and a timer be armed and most important of all which
other block device of the group must resume the work once the timer is fired.

The advantages of this algorigthm are:

-only one timer active at a given time (no more cpu usage than regular throttling)
-no central place didacting the sheduling policy like a didactureship:
 we love collaboration isn't it ?:)
-No need to deal with  incoming queues to collect requests before scheduling 
 then with and dispatchs queues
-Compatible with the throttling code with almost no changes
-As you go scheduling

Best regards

Benoît

Benoît Canet (8):
  throttle: Extract timers from ThrottleState into a separate
    ThrottleTimers structure
  throttle: Add throttle group infrastructure
  throttle: Add throttle group infrastructure tests
  throttle: Prepare to have multiple timers for one ThrottleState
  throttle: Add a way to know if throttle_schedule_timer had armed a
    timer
  throttle: Add a way to fire one of the timers asap like a bottom half
  throttle: Add throttle group support
  throttle: Update throttle infrastructure copyright

 block.c                         | 211 ++++++++++++++++++++++++++++++++++-----
 block/Makefile.objs             |   1 +
 block/qapi.c                    |   7 +-
 block/throttle-groups.c         | 212 ++++++++++++++++++++++++++++++++++++++++
 blockdev.c                      |  18 +++-
 hmp.c                           |   4 +-
 include/block/block.h           |   3 +-
 include/block/block_int.h       |   9 +-
 include/block/throttle-groups.h |  45 +++++++++
 include/qemu/throttle.h         |  46 ++++++---
 qapi/block-core.json            |   5 +-
 qemu-options.hx                 |   1 +
 qmp-commands.hx                 |   3 +-
 tests/test-throttle.c           | 137 +++++++++++++++++++-------
 util/throttle.c                 | 107 +++++++++++++-------
 15 files changed, 684 insertions(+), 125 deletions(-)
 create mode 100644 block/throttle-groups.c
 create mode 100644 include/block/throttle-groups.h

-- 
2.1.0.rc1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC V2 1/8] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 2/8] throttle: Add throttle group infrastructure Benoît Canet
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît 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.canet@nodalink.com>
---
 block.c                   | 35 ++++++++++++--------
 include/block/block_int.h |  1 +
 include/qemu/throttle.h   | 36 +++++++++++++--------
 tests/test-throttle.c     | 82 ++++++++++++++++++++++++++---------------------
 util/throttle.c           | 73 ++++++++++++++++++++++++-----------------
 5 files changed, 134 insertions(+), 93 deletions(-)

diff --git a/block.c b/block.c
index 8cf519b..412970d 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;
     }
 
@@ -1969,6 +1973,9 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
     memcpy(&bs_dest->throttle_state,
            &bs_src->throttle_state,
            sizeof(ThrottleState));
+    memcpy(&bs_dest->throttle_timers,
+           &bs_src->throttle_timers,
+           sizeof(ThrottleTimers));
     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;
@@ -2031,7 +2038,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 +2056,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 +5670,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 +5704,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] 12+ messages in thread

* [Qemu-devel] [RFC V2 2/8] throttle: Add throttle group infrastructure
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 1/8] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 3/8] throttle: Add throttle group infrastructure tests Benoît Canet
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît 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.canet@nodalink.com>
---
 block/Makefile.objs             |   1 +
 block/throttle-groups.c         | 212 ++++++++++++++++++++++++++++++++++++++++
 include/block/block_int.h       |   1 +
 include/block/throttle-groups.h |  45 +++++++++
 4 files changed, 259 insertions(+)
 create mode 100644 block/throttle-groups.c
 create mode 100644 include/block/throttle-groups.h

diff --git a/block/Makefile.objs b/block/Makefile.objs
index fd88c03..72ce2d3 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -9,6 +9,7 @@ block-obj-y += snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
 block-obj-$(CONFIG_POSIX) += raw-posix.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
+block-obj-y += throttle-groups.o
 
 ifeq ($(CONFIG_POSIX),y)
 block-obj-y += nbd.o nbd-client.o sheepdog.o
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
new file mode 100644
index 0000000..ea5baca
--- /dev/null
+++ b/block/throttle-groups.c
@@ -0,0 +1,212 @@
+/*
+ * QEMU block throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ *
+ * Author:
+ *   Benoît Canet <benoit.canet@nodalink.com>
+ *
+ * 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 "block/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);
+    pstrcpy(tg->name, sizeof(tg->name), name);
+    qemu_mutex_init(&tg->lock);
+    throttle_init(&tg->ts);
+    QLIST_INIT(&tg->head);
+    tg->refcount = 1;
+
+    /* 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;
+}
+
+/* Compare a name with a given ThrottleState group name
+ *
+ * @ts:   the throttle state whose group we are inspecting
+ * @name: the name to compare
+ * @ret:  true if names are equal else false
+ */
+bool throttle_group_compare(ThrottleState *ts, const char *name)
+{
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+
+    if (!name) {
+        return false;
+    }
+
+    return !strcmp(name, tg->name);
+}
+
+/* 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);
+}
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/block/throttle-groups.h b/include/block/throttle-groups.h
new file mode 100644
index 0000000..d000067
--- /dev/null
+++ b/include/block/throttle-groups.h
@@ -0,0 +1,45 @@
+/*
+ * QEMU block throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ *
+ * Author:
+ *   Benoît Canet <benoit.canet@nodalink.com>
+ *
+ * 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);
+
+bool throttle_group_compare(ThrottleState *ts, const char *name);
+
+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
-- 
2.1.0.rc1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC V2 3/8] throttle: Add throttle group infrastructure tests
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 1/8] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 2/8] throttle: Add throttle group infrastructure Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 4/8] throttle: Prepare to have multiple timers for one ThrottleState Benoît Canet
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît Canet, stefanha

Signed-off-by: Benoit Canet <benoit.canet@nodalink.com>
---
 tests/test-throttle.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 66e4982..50e6655 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 "block/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] 12+ messages in thread

* [Qemu-devel] [RFC V2 4/8] throttle: Prepare to have multiple timers for one ThrottleState
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
                   ` (2 preceding siblings ...)
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 3/8] throttle: Add throttle group infrastructure tests Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 5/8] throttle: Add a way to know if throttle_schedule_timer had armed a timer Benoît Canet
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît 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.canet@nodalink.com>
---
 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 412970d..5d48c35 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] 12+ messages in thread

* [Qemu-devel] [RFC V2 5/8] throttle: Add a way to know if throttle_schedule_timer had armed a timer
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
                   ` (3 preceding siblings ...)
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 4/8] throttle: Prepare to have multiple timers for one ThrottleState Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 6/8] throttle: Add a way to fire one of the timers asap like a bottom half Benoît Canet
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît Canet, stefanha

This will be needed by the group throttling patches for the algorithm to be
accurate.

Signed-off-by: Benoit Canet <benoit.canet@nodalink.com>
---
 block.c                 |  7 +++++--
 include/qemu/throttle.h |  3 ++-
 util/throttle.c         | 12 +++++++-----
 3 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/block.c b/block.c
index 5d48c35..323fca5 100644
--- a/block.c
+++ b/block.c
@@ -199,10 +199,13 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
                                      unsigned int bytes,
                                      bool is_write)
 {
+    bool armed;
+
     /* does this io must wait */
     bool must_wait = throttle_schedule_timer(&bs->throttle_state,
                                              &bs->throttle_timers,
-                                             is_write);
+                                             is_write,
+                                             &armed);
 
     /* if must wait or any request of this type throttled queue the IO */
     if (must_wait ||
@@ -216,7 +219,7 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
 
     /* if the next request must wait -> do nothing */
     if (throttle_schedule_timer(&bs->throttle_state, &bs->throttle_timers,
-                                is_write)) {
+                                is_write, &armed)) {
         return;
     }
 
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 3aece3a..3a16c48 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -124,7 +124,8 @@ 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_timer_fired(ThrottleState *ts, bool is_write);
 
diff --git a/util/throttle.c b/util/throttle.c
index 0e305e3..a273acb 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,12 +394,12 @@ 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;
 }
 
-- 
2.1.0.rc1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC V2 6/8] throttle: Add a way to fire one of the timers asap like a bottom half
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
                   ` (4 preceding siblings ...)
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 5/8] throttle: Add a way to know if throttle_schedule_timer had armed a timer Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 7/8] throttle: Add throttle group support Benoît Canet
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît Canet, stefanha

This will be needed by the group throttling algorithm.

Signed-off-by: Benoit Canet <benoit.canet@nodalink.com>
---
 include/qemu/throttle.h |  2 ++
 util/throttle.c         | 11 +++++++++++
 2 files changed, 13 insertions(+)

diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 3a16c48..3b9d1b8 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -127,6 +127,8 @@ bool throttle_schedule_timer(ThrottleState *ts,
                              bool is_write,
                              bool *armed);
 
+void throttle_fire_timer(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);
diff --git a/util/throttle.c b/util/throttle.c
index a273acb..163b9d0 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -403,6 +403,17 @@ bool throttle_schedule_timer(ThrottleState *ts,
     return true;
 }
 
+/* Schedule a throttle timer like a BH
+ *
+ * @tt:       The timers structure
+ * @is_write: the type of operation (read/write)
+ */
+void throttle_fire_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] 12+ messages in thread

* [Qemu-devel] [RFC V2 7/8] throttle: Add throttle group support
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
                   ` (5 preceding siblings ...)
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 6/8] throttle: Add a way to fire one of the timers asap like a bottom half Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-09-02 22:37   ` Eric Blake
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 8/8] throttle: Update throttle infrastructure copyright Benoît Canet
  2014-08-20 14:27 ` [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
  8 siblings, 1 reply; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît Canet, stefanha

The throttle group support use a cooperative round robin scheduling algorithm.

The principle of the algorithm are simple:
- Each BDS of the group is used as a token in a circular way.
- The active BDS compute if a wait must be done and arm the right timer.
- If a wait must be done the token timer will be armed so the token will become
  the next active BDS.

Signed-off-by: Benoit Canet <benoit.canet@nodalink.com>
---
 block.c                   | 191 ++++++++++++++++++++++++++++++++++++++++------
 block/qapi.c              |   7 +-
 block/throttle-groups.c   |   2 +-
 blockdev.c                |  18 ++++-
 hmp.c                     |   4 +-
 include/block/block.h     |   3 +-
 include/block/block_int.h |   9 ++-
 qapi/block-core.json      |   5 +-
 qemu-options.hx           |   1 +
 qmp-commands.hx           |   3 +-
 10 files changed, 208 insertions(+), 35 deletions(-)

diff --git a/block.c b/block.c
index 323fca5..6dd94fb 100644
--- a/block.c
+++ b/block.c
@@ -35,6 +35,7 @@
 #include "qmp-commands.h"
 #include "qemu/timer.h"
 #include "qapi-event.h"
+#include "block/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,99 @@ 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);
+            /* take care of the case where bs is the only bs of the group */
+            if (token == bs) {
+                token = NULL;
+            }
+            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)
 {
+
+    throttle_group_lock(bs->throttle_state);
     bs->io_limits_enabled = false;
+    throttle_group_unlock(bs->throttle_state);
 
     bdrv_start_throttled_reqs(bs);
 
+    throttle_group_lock(bs->throttle_state);
+    bdrv_throttle_group_remove(bs);
+    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 +256,53 @@ void bdrv_io_limits_enable(BlockDriverState *bs)
                          bdrv_throttle_write_timer_cb,
                          bs);
     bs->io_limits_enabled = true;
+    throttle_group_unlock(bs->throttle_state);
+}
+
+void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group)
+{
+    /* this bs is not part of any group */
+    if (!bs->throttle_state) {
+        return;
+    }
+
+    /* this bs is a part of the same group than the one we want */
+    if (throttle_group_compare(bs->throttle_state, group)) {
+        return;
+    }
+
+    /* need to change the group this bs belong to */
+    bdrv_io_limits_disable(bs);
+    bdrv_io_limits_enable(bs, group);
+}
+
+/* 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);
+
+    /* get next bs round in round robin style */
+    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 no IO are queued for scheduling on the next round robin token
+     * then decide the token is the current bs because chances are
+     * the current bs get the current request queued.
+     */
+    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,32 +314,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);
+    /* get the next bs to schedule */
+    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,
+    bool must_wait = throttle_schedule_timer(bs->throttle_state,
+                                             &token->throttle_timers,
                                              is_write,
                                              &armed);
+    /* the timer got armed -> save the token */
+    if (armed) {
+        throttle_group_set_token(bs->throttle_state, token, is_write);
+    }
+    empty = qemu_co_queue_empty(&bs->throttled_reqs[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]);
     }
 
-    /* the IO will be executed, do the accounting */
-    throttle_account(&bs->throttle_state, is_write, bytes);
-
+    throttle_group_lock(bs->throttle_state);
+    /* get the next bs to schedule */
+    token = bdrv_next_throttle_token(bs, is_write);
+    /* is there an IO to schedule in the round robin token ? */
+    token_queue_empty = qemu_co_queue_empty(&token->throttled_reqs[is_write]);
+    /* this IO will be executed, do the accounting */
+    throttle_account(bs->throttle_state, is_write, bytes);
+    /* does the next IO queued must wait ? */
+    must_wait = throttle_schedule_timer(bs->throttle_state,
+                                        &token->throttle_timers,
+                                        is_write,
+                                        &armed);
+    /* If a timer was armed or an IO is to be scheduled in the next round robin
+     * token then save the token.
+     */
+    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, &armed)) {
+    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]);
+    /* else schedule next request for execution */
+    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_fire_timer(&token->throttle_timers, is_write);
+    }
+    throttle_group_unlock(bs->throttle_state);
 }
 
 size_t bdrv_opt_mem_align(BlockDriverState *bs)
@@ -1975,15 +2121,16 @@ 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->io_limits_enabled  = bs_src->io_limits_enabled;
+    bs_dest->throttled_reqs[0]  = bs_src->throttled_reqs[0];
+    bs_dest->throttled_reqs[1]  = bs_src->throttled_reqs[1];
+    memcpy(&bs_dest->round_robin,
+           &bs_src->round_robin,
+           sizeof(bs_dest->round_robin));
     memcpy(&bs_dest->throttle_timers,
            &bs_src->throttle_timers,
            sizeof(ThrottleTimers));
-    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;
 
     /* r/w error */
     bs_dest->on_read_error      = bs_src->on_read_error;
diff --git a/block/qapi.c b/block/qapi.c
index f44f6b4..c1b92c3 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -24,6 +24,7 @@
 
 #include "block/qapi.h"
 #include "block/block_int.h"
+#include "block/throttle-groups.h"
 #include "qmp-commands.h"
 #include "qapi-visit.h"
 #include "qapi/qmp-output-visitor.h"
@@ -54,7 +55,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
 
     if (bs->io_limits_enabled) {
         ThrottleConfig cfg;
-        throttle_get_config(&bs->throttle_state, &cfg);
+
+        throttle_group_lock(bs->throttle_state);
+        throttle_get_config(bs->throttle_state, &cfg);
+        throttle_group_unlock(bs->throttle_state);
+
         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/block/throttle-groups.c b/block/throttle-groups.c
index ea5baca..399ae5e 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -154,7 +154,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/blockdev.c b/blockdev.c
index 48bd9a3..b9ed099 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,9 +1747,11 @@ 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);
+    } else if (bs->io_limits_enabled && throttle_enabled(&cfg)) {
+        bdrv_io_limits_update_group(bs, has_group ? group : NULL);
     }
 
     if (bs->io_limits_enabled) {
@@ -2643,6 +2651,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..70fce04 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -190,8 +190,9 @@ 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_io_limits_update_group(BlockDriverState *bs, const char *group);
 
 void bdrv_init(void);
 void bdrv_init_with_whitelist(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/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:
 
-- 
2.1.0.rc1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC V2 8/8] throttle: Update throttle infrastructure copyright
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
                   ` (6 preceding siblings ...)
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 7/8] throttle: Add throttle group support Benoît Canet
@ 2014-08-13 14:23 ` Benoît Canet
  2014-08-20 14:27 ` [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
  8 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-08-13 14:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, pbonzini, Benoît Canet, stefanha

Signed-off-by: Benoit Canet <benoit.canet@nodalink.com>
---
 include/qemu/throttle.h | 4 ++--
 tests/test-throttle.c   | 4 ++--
 util/throttle.c         | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 3b9d1b8..8abd94d 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -1,10 +1,10 @@
 /*
  * QEMU throttling infrastructure
  *
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
  *
  * Author:
- *   Benoît Canet <benoit.canet@irqsave.net>
+ *   Benoît Canet <benoit.canet@nodalink.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 50e6655..18cc1ab 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -1,10 +1,10 @@
 /*
  * Throttle infrastructure tests
  *
- * Copyright Nodalink, SARL. 2013
+ * Copyright Nodalink, EURL. 2013-2014
  *
  * Authors:
- *  Benoît Canet     <benoit.canet@irqsave.net>
+ *  Benoît Canet     <benoit.canet@nodalink.com>
  *
  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  * See the COPYING.LIB file in the top-level directory.
diff --git a/util/throttle.c b/util/throttle.c
index 163b9d0..4884c08 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -1,10 +1,10 @@
 /*
  * QEMU throttling infrastructure
  *
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
  *
  * Author:
- *   Benoît Canet <benoit.canet@irqsave.net>
+ *   Benoît Canet <benoit.canet@nodalink.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
-- 
2.1.0.rc1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling
  2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
                   ` (7 preceding siblings ...)
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 8/8] throttle: Update throttle infrastructure copyright Benoît Canet
@ 2014-08-20 14:27 ` Benoît Canet
  2014-09-01 14:34   ` Benoît Canet
  8 siblings, 1 reply; 12+ messages in thread
From: Benoît Canet @ 2014-08-20 14:27 UTC (permalink / raw)
  To: Benoît Canet; +Cc: kwolf, pbonzini, qemu-devel, stefanha

On Wed, Aug 13, 2014 at 04:23:51PM +0200, Benoît Canet wrote:
> Hi,
> 
> Here is my current wip on the throttle group support.
> 
> For the user interface I implemented Stefanha's idea proposed in Stuttgart.
> 
> For the throttling algorithm I use a cooperative round robin scheduler.
> 
> Classical round robin works with a fixed HZ ticks and it's totaly incompatible
> with the throttling algorithm.
> 
> So the cooperative round robin scheduler is a way for each block device to decide
> if a pause must be done and a timer be armed and most important of all which
> other block device of the group must resume the work once the timer is fired.
> 
> The advantages of this algorigthm are:
> 
> -only one timer active at a given time (no more cpu usage than regular throttling)
> -no central place didacting the sheduling policy like a didactureship:
>  we love collaboration isn't it ?:)
> -No need to deal with  incoming queues to collect requests before scheduling 
>  then with and dispatchs queues
> -Compatible with the throttling code with almost no changes
> -As you go scheduling
> 
> Best regards
> 
> Benoît
> 
> Benoît Canet (8):
>   throttle: Extract timers from ThrottleState into a separate
>     ThrottleTimers structure
>   throttle: Add throttle group infrastructure
>   throttle: Add throttle group infrastructure tests
>   throttle: Prepare to have multiple timers for one ThrottleState
>   throttle: Add a way to know if throttle_schedule_timer had armed a
>     timer
>   throttle: Add a way to fire one of the timers asap like a bottom half
>   throttle: Add throttle group support
>   throttle: Update throttle infrastructure copyright
> 
>  block.c                         | 211 ++++++++++++++++++++++++++++++++++-----
>  block/Makefile.objs             |   1 +
>  block/qapi.c                    |   7 +-
>  block/throttle-groups.c         | 212 ++++++++++++++++++++++++++++++++++++++++
>  blockdev.c                      |  18 +++-
>  hmp.c                           |   4 +-
>  include/block/block.h           |   3 +-
>  include/block/block_int.h       |   9 +-
>  include/block/throttle-groups.h |  45 +++++++++
>  include/qemu/throttle.h         |  46 ++++++---
>  qapi/block-core.json            |   5 +-
>  qemu-options.hx                 |   1 +
>  qmp-commands.hx                 |   3 +-
>  tests/test-throttle.c           | 137 +++++++++++++++++++-------
>  util/throttle.c                 | 107 +++++++++++++-------
>  15 files changed, 684 insertions(+), 125 deletions(-)
>  create mode 100644 block/throttle-groups.c
>  create mode 100644 include/block/throttle-groups.h
> 
> -- 
> 2.1.0.rc1
> 

ping

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling
  2014-08-20 14:27 ` [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
@ 2014-09-01 14:34   ` Benoît Canet
  0 siblings, 0 replies; 12+ messages in thread
From: Benoît Canet @ 2014-09-01 14:34 UTC (permalink / raw)
  To: Benoît Canet; +Cc: kwolf, pbonzini, qemu-devel, stefanha

On Wed, Aug 20, 2014 at 02:27:06PM +0000, Benoît Canet wrote:
> On Wed, Aug 13, 2014 at 04:23:51PM +0200, Benoît Canet wrote:
> > Hi,
> > 
> > Here is my current wip on the throttle group support.
> > 
> > For the user interface I implemented Stefanha's idea proposed in Stuttgart.
> > 
> > For the throttling algorithm I use a cooperative round robin scheduler.
> > 
> > Classical round robin works with a fixed HZ ticks and it's totaly incompatible
> > with the throttling algorithm.
> > 
> > So the cooperative round robin scheduler is a way for each block device to decide
> > if a pause must be done and a timer be armed and most important of all which
> > other block device of the group must resume the work once the timer is fired.
> > 
> > The advantages of this algorigthm are:
> > 
> > -only one timer active at a given time (no more cpu usage than regular throttling)
> > -no central place didacting the sheduling policy like a didactureship:
> >  we love collaboration isn't it ?:)
> > -No need to deal with  incoming queues to collect requests before scheduling 
> >  then with and dispatchs queues
> > -Compatible with the throttling code with almost no changes
> > -As you go scheduling
> > 
> > Best regards
> > 
> > Benoît
> > 
> > Benoît Canet (8):
> >   throttle: Extract timers from ThrottleState into a separate
> >     ThrottleTimers structure
> >   throttle: Add throttle group infrastructure
> >   throttle: Add throttle group infrastructure tests
> >   throttle: Prepare to have multiple timers for one ThrottleState
> >   throttle: Add a way to know if throttle_schedule_timer had armed a
> >     timer
> >   throttle: Add a way to fire one of the timers asap like a bottom half
> >   throttle: Add throttle group support
> >   throttle: Update throttle infrastructure copyright
> > 
> >  block.c                         | 211 ++++++++++++++++++++++++++++++++++-----
> >  block/Makefile.objs             |   1 +
> >  block/qapi.c                    |   7 +-
> >  block/throttle-groups.c         | 212 ++++++++++++++++++++++++++++++++++++++++
> >  blockdev.c                      |  18 +++-
> >  hmp.c                           |   4 +-
> >  include/block/block.h           |   3 +-
> >  include/block/block_int.h       |   9 +-
> >  include/block/throttle-groups.h |  45 +++++++++
> >  include/qemu/throttle.h         |  46 ++++++---
> >  qapi/block-core.json            |   5 +-
> >  qemu-options.hx                 |   1 +
> >  qmp-commands.hx                 |   3 +-
> >  tests/test-throttle.c           | 137 +++++++++++++++++++-------
> >  util/throttle.c                 | 107 +++++++++++++-------
> >  15 files changed, 684 insertions(+), 125 deletions(-)
> >  create mode 100644 block/throttle-groups.c
> >  create mode 100644 include/block/throttle-groups.h
> > 
> > -- 
> > 2.1.0.rc1
> > 
> 
> ping

ping

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [RFC V2 7/8] throttle: Add throttle group support
  2014-08-13 14:23 ` [Qemu-devel] [RFC V2 7/8] throttle: Add throttle group support Benoît Canet
@ 2014-09-02 22:37   ` Eric Blake
  0 siblings, 0 replies; 12+ messages in thread
From: Eric Blake @ 2014-09-02 22:37 UTC (permalink / raw)
  To: Benoît Canet, qemu-devel; +Cc: kwolf, pbonzini, stefanha

[-- Attachment #1: Type: text/plain, Size: 944 bytes --]

On 08/13/2014 08:23 AM, Benoît Canet wrote:
> The throttle group support use a cooperative round robin scheduling algorithm.
> 
> The principle of the algorithm are simple:

s/are/is/

> - Each BDS of the group is used as a token in a circular way.
> - The active BDS compute if a wait must be done and arm the right timer.

s/compute/computes/
s/arm/arms/

> - If a wait must be done the token timer will be armed so the token will become
>   the next active BDS.
> 
> Signed-off-by: Benoit Canet <benoit.canet@nodalink.com>
> ---

> +++ 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)

Extra blank comment line.

Interface seems okay; I haven't looked closely at the code.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 539 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2014-09-02 22:37 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-13 14:23 [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 1/8] throttle: Extract timers from ThrottleState into a separate ThrottleTimers structure Benoît Canet
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 2/8] throttle: Add throttle group infrastructure Benoît Canet
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 3/8] throttle: Add throttle group infrastructure tests Benoît Canet
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 4/8] throttle: Prepare to have multiple timers for one ThrottleState Benoît Canet
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 5/8] throttle: Add a way to know if throttle_schedule_timer had armed a timer Benoît Canet
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 6/8] throttle: Add a way to fire one of the timers asap like a bottom half Benoît Canet
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 7/8] throttle: Add throttle group support Benoît Canet
2014-09-02 22:37   ` Eric Blake
2014-08-13 14:23 ` [Qemu-devel] [RFC V2 8/8] throttle: Update throttle infrastructure copyright Benoît Canet
2014-08-20 14:27 ` [Qemu-devel] [RFC V2 0/8] Throttle group cooperative round robin scheduling Benoît Canet
2014-09-01 14:34   ` Benoît Canet

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.