All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver
@ 2017-06-23 12:46 Manos Pitsidianakis
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
                   ` (8 more replies)
  0 siblings, 9 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

This patch series adds a filter driver that uses the existing I/O throttle
code. Throttle groups can be then created and managed with QOM.

The 'remove legacy I/O throttling' patch replaces existing interface (command
line, qmp, hmp) to use the throttle filter transparently so as to not break
backwards compatibility. The created throttle node is stored in a field in
BlockBackendPublic so that the legacy interface can access it. The patch
currently breaks test 129 which commits, mirrors and backups a device with
legacy throttling options. I assume the internal throttling node affects this,
so I'd like some advice on how to approach this.

Test 139 could be updated to include the throttle driver though I'm not sure if
it's needed.

I'm also not sure about the test I created for this (last patch).

v2: 
- Move throttle group membership to ThrottleGroupMember struct
v3:
- add ThrottleGroup object
- convert legacy interface to use the driver
- add tests


Manos Pitsidianakis (8):
  block: move ThrottleGroup membership to ThrottleGroupMember
  block: Add aio_context field in ThrottleGroupMember
  block: add throttle block filter driver
  block: convert ThrottleGroup to object with QOM
  block: add BlockDevOptionsThrottle to QAPI
  block: add options parameter to bdrv_new_open_driver()
  block: remove legacy I/O throttling
  block: add throttle block filter driver interface tests

 block.c                         |  13 +-
 block/Makefile.objs             |   1 +
 block/block-backend.c           | 151 +++++----
 block/commit.c                  |   4 +-
 block/mirror.c                  |   2 +-
 block/qapi.c                    |   8 +-
 block/throttle-groups.c         | 660 ++++++++++++++++++++++++++++++----------
 block/throttle.c                | 431 ++++++++++++++++++++++++++
 block/vvfat.c                   |   2 +-
 blockdev.c                      |  55 +++-
 include/block/block.h           |   2 +-
 include/block/throttle-groups.h |  15 +-
 include/qemu/throttle-options.h |  60 ++--
 include/qemu/throttle.h         |  31 ++
 include/sysemu/block-backend.h  |  26 +-
 qapi/block-core.json            |  19 +-
 tests/qemu-iotests/184          | 144 +++++++++
 tests/qemu-iotests/184.out      |  31 ++
 tests/qemu-iotests/group        |   1 +
 tests/test-throttle.c           | 125 ++++----
 20 files changed, 1441 insertions(+), 340 deletions(-)
 create mode 100644 block/throttle.c
 create mode 100755 tests/qemu-iotests/184
 create mode 100644 tests/qemu-iotests/184.out

-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
@ 2017-06-23 12:46 ` Manos Pitsidianakis
  2017-06-26 13:23   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-06-27 12:08   ` [Qemu-devel] " Alberto Garcia
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember Manos Pitsidianakis
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

This commit gathers ThrottleGroup membership details from
BlockBackendPublic into ThrottleGroupMember and refactors existing code
to use the structure.

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/block-backend.c           |  66 +++++----
 block/qapi.c                    |   8 +-
 block/throttle-groups.c         | 304 ++++++++++++++++++++--------------------
 blockdev.c                      |   4 +-
 include/block/throttle-groups.h |  15 +-
 include/qemu/throttle.h         |  26 ++++
 include/sysemu/block-backend.h  |  20 +--
 tests/test-throttle.c           |  53 +++----
 8 files changed, 260 insertions(+), 236 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index a2bbae90b1..90a7abaa53 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -216,9 +216,9 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
     blk->shared_perm = shared_perm;
     blk_set_enable_write_cache(blk, true);
 
-    qemu_co_mutex_init(&blk->public.throttled_reqs_lock);
-    qemu_co_queue_init(&blk->public.throttled_reqs[0]);
-    qemu_co_queue_init(&blk->public.throttled_reqs[1]);
+    qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock);
+    qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
+    qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
     block_acct_init(&blk->stats);
 
     notifier_list_init(&blk->remove_bs_notifiers);
@@ -286,7 +286,7 @@ static void blk_delete(BlockBackend *blk)
     assert(!blk->refcnt);
     assert(!blk->name);
     assert(!blk->dev);
-    if (blk->public.throttle_state) {
+    if (blk->public.throttle_group_member.throttle_state) {
         blk_io_limits_disable(blk);
     }
     if (blk->root) {
@@ -597,9 +597,12 @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
  */
 void blk_remove_bs(BlockBackend *blk)
 {
+    ThrottleTimers *tt;
+
     notifier_list_notify(&blk->remove_bs_notifiers, blk);
-    if (blk->public.throttle_state) {
-        throttle_timers_detach_aio_context(&blk->public.throttle_timers);
+    if (blk->public.throttle_group_member.throttle_state) {
+        tt = &blk->public.throttle_group_member.throttle_timers;
+        throttle_timers_detach_aio_context(tt);
     }
 
     blk_update_root_state(blk);
@@ -621,9 +624,10 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
     bdrv_ref(bs);
 
     notifier_list_notify(&blk->insert_bs_notifiers, blk);
-    if (blk->public.throttle_state) {
+    if (blk->public.throttle_group_member.throttle_state) {
         throttle_timers_attach_aio_context(
-            &blk->public.throttle_timers, bdrv_get_aio_context(bs));
+            &blk->public.throttle_group_member.throttle_timers,
+            bdrv_get_aio_context(bs));
     }
 
     return 0;
@@ -985,8 +989,9 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
     bdrv_inc_in_flight(bs);
 
     /* throttling disk I/O */
-    if (blk->public.throttle_state) {
-        throttle_group_co_io_limits_intercept(blk, bytes, false);
+    if (blk->public.throttle_group_member.throttle_state) {
+        throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
+                bytes, false);
     }
 
     ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags);
@@ -1009,10 +1014,10 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
     }
 
     bdrv_inc_in_flight(bs);
-
     /* throttling disk I/O */
-    if (blk->public.throttle_state) {
-        throttle_group_co_io_limits_intercept(blk, bytes, true);
+    if (blk->public.throttle_group_member.throttle_state) {
+        throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
+                bytes, true);
     }
 
     if (!blk->enable_write_cache) {
@@ -1681,15 +1686,17 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
 void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
 {
     BlockDriverState *bs = blk_bs(blk);
+    ThrottleTimers *tt;
 
     if (bs) {
-        if (blk->public.throttle_state) {
-            throttle_timers_detach_aio_context(&blk->public.throttle_timers);
+        if (blk->public.throttle_group_member.throttle_state) {
+            tt = &blk->public.throttle_group_member.throttle_timers;
+            throttle_timers_detach_aio_context(tt);
         }
         bdrv_set_aio_context(bs, new_context);
-        if (blk->public.throttle_state) {
-            throttle_timers_attach_aio_context(&blk->public.throttle_timers,
-                                               new_context);
+        if (blk->public.throttle_group_member.throttle_state) {
+            tt = &blk->public.throttle_group_member.throttle_timers;
+            throttle_timers_attach_aio_context(tt, new_context);
         }
     }
 }
@@ -1907,33 +1914,34 @@ int blk_commit_all(void)
 /* throttling disk I/O limits */
 void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
 {
-    throttle_group_config(blk, cfg);
+    throttle_group_config(&blk->public.throttle_group_member, cfg);
 }
 
 void blk_io_limits_disable(BlockBackend *blk)
 {
-    assert(blk->public.throttle_state);
+    assert(blk->public.throttle_group_member.throttle_state);
     bdrv_drained_begin(blk_bs(blk));
-    throttle_group_unregister_blk(blk);
+    throttle_group_unregister_tgm(&blk->public.throttle_group_member);
     bdrv_drained_end(blk_bs(blk));
 }
 
 /* should be called before blk_set_io_limits if a limit is set */
 void blk_io_limits_enable(BlockBackend *blk, const char *group)
 {
-    assert(!blk->public.throttle_state);
-    throttle_group_register_blk(blk, group);
+    assert(!blk->public.throttle_group_member.throttle_state);
+    throttle_group_register_tgm(&blk->public.throttle_group_member, group);
 }
 
 void blk_io_limits_update_group(BlockBackend *blk, const char *group)
 {
     /* this BB is not part of any group */
-    if (!blk->public.throttle_state) {
+    if (!blk->public.throttle_group_member.throttle_state) {
         return;
     }
 
     /* this BB is a part of the same group than the one we want */
-    if (!g_strcmp0(throttle_group_get_name(blk), group)) {
+    if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member),
+                group)) {
         return;
     }
 
@@ -1955,8 +1963,8 @@ static void blk_root_drained_begin(BdrvChild *child)
     /* Note that blk->root may not be accessible here yet if we are just
      * attaching to a BlockDriverState that is drained. Use child instead. */
 
-    if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) {
-        throttle_group_restart_blk(blk);
+    if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) {
+        throttle_group_restart_tgm(&blk->public.throttle_group_member);
     }
 }
 
@@ -1965,8 +1973,8 @@ static void blk_root_drained_end(BdrvChild *child)
     BlockBackend *blk = child->opaque;
     assert(blk->quiesce_counter);
 
-    assert(blk->public.io_limits_disabled);
-    atomic_dec(&blk->public.io_limits_disabled);
+    assert(blk->public.throttle_group_member.io_limits_disabled);
+    atomic_dec(&blk->public.throttle_group_member.io_limits_disabled);
 
     if (--blk->quiesce_counter == 0) {
         if (blk->dev_ops && blk->dev_ops->drained_end) {
diff --git a/block/qapi.c b/block/qapi.c
index 0a41d59bf3..70ec5552be 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -67,10 +67,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
     info->backing_file_depth = bdrv_get_backing_file_depth(bs);
     info->detect_zeroes = bs->detect_zeroes;
 
-    if (blk && blk_get_public(blk)->throttle_state) {
+    if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) {
         ThrottleConfig cfg;
+        BlockBackendPublic *blkp = blk_get_public(blk);
 
-        throttle_group_get_config(blk, &cfg);
+        throttle_group_get_config(&blkp->throttle_group_member, &cfg);
 
         info->bps     = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
         info->bps_rd  = cfg.buckets[THROTTLE_BPS_READ].avg;
@@ -118,7 +119,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
         info->iops_size = cfg.op_size;
 
         info->has_group = true;
-        info->group = g_strdup(throttle_group_get_name(blk));
+        info->group =
+            g_strdup(throttle_group_get_name(&blkp->throttle_group_member));
     }
 
     info->write_threshold = bdrv_write_threshold_get(bs);
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index a181cb1dee..5e9d8fb4d6 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -30,7 +30,7 @@
 #include "sysemu/qtest.h"
 
 /* The ThrottleGroup structure (with its ThrottleState) is shared
- * among different BlockBackends and it's independent from
+ * among different ThrottleGroupMembers and it's independent from
  * AioContext, so in order to use it from different threads it needs
  * its own locking.
  *
@@ -40,7 +40,7 @@
  * The whole ThrottleGroup structure is private and invisible to
  * outside users, that only use it through its ThrottleState.
  *
- * In addition to the ThrottleGroup structure, BlockBackendPublic has
+ * In addition to the ThrottleGroup structure, ThrottleGroupMember has
  * fields that need to be accessed by other members of the group and
  * therefore also need to be protected by this lock. Once a
  * BlockBackend is registered in a group those fields can be accessed
@@ -58,8 +58,8 @@ typedef struct ThrottleGroup {
 
     QemuMutex lock; /* This lock protects the following four fields */
     ThrottleState ts;
-    QLIST_HEAD(, BlockBackendPublic) head;
-    BlockBackend *tokens[2];
+    QLIST_HEAD(, ThrottleGroupMember) head;
+    ThrottleGroupMember *tokens[2];
     bool any_timer_armed[2];
 
     /* These two are protected by the global throttle_groups_lock */
@@ -133,114 +133,112 @@ void throttle_group_unref(ThrottleState *ts)
     qemu_mutex_unlock(&throttle_groups_lock);
 }
 
-/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer)
+/* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
  * is guaranteed to remain constant during the lifetime of the group.
  *
- * @blk:  a BlockBackend that is member of a throttling group
+ * @tgm:  a ThrottleGroupMember
  * @ret:  the name of the group.
  */
-const char *throttle_group_get_name(BlockBackend *blk)
+const char *throttle_group_get_name(ThrottleGroupMember *tgm)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+    ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
     return tg->name;
 }
 
-/* Return the next BlockBackend in the round-robin sequence, simulating a
- * circular list.
+/* Return the next ThrottleGroupMember in the round-robin sequence, simulating
+ * a circular list.
  *
  * This assumes that tg->lock is held.
  *
- * @blk: the current BlockBackend
- * @ret: the next BlockBackend in the sequence
+ * @tgm: the current ThrottleGroupMember
+ * @ret: the next ThrottleGroupMember in the sequence
  */
-static BlockBackend *throttle_group_next_blk(BlockBackend *blk)
+static ThrottleGroupMember *throttle_group_next_tgm(ThrottleGroupMember *tgm)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleState *ts = blkp->throttle_state;
+    ThrottleState *ts = tgm->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
-    BlockBackendPublic *next = QLIST_NEXT(blkp, round_robin);
+    ThrottleGroupMember *next = QLIST_NEXT(tgm, round_robin);
 
     if (!next) {
         next = QLIST_FIRST(&tg->head);
     }
 
-    return blk_by_public(next);
+    return next;
 }
 
 /*
- * Return whether a BlockBackend has pending requests.
+ * Return whether a ThrottleGroupMember has pending requests.
  *
  * This assumes that tg->lock is held.
  *
- * @blk: the BlockBackend
- * @is_write:  the type of operation (read/write)
- * @ret:       whether the BlockBackend has pending requests.
+ * @tgm:        the ThrottleGroupMember
+ * @is_write:   the type of operation (read/write)
+ * @ret:        whether the ThrottleGroupMember has pending requests.
  */
-static inline bool blk_has_pending_reqs(BlockBackend *blk,
+static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm,
                                         bool is_write)
 {
-    const BlockBackendPublic *blkp = blk_get_public(blk);
-    return blkp->pending_reqs[is_write];
+    return tgm->pending_reqs[is_write];
 }
 
-/* Return the next BlockBackend in the round-robin sequence with pending I/O
- * requests.
+/* Return the next ThrottleGroupMember in the round-robin sequence with pending
+ * I/O requests.
  *
  * This assumes that tg->lock is held.
  *
- * @blk:       the current BlockBackend
+ * @tgm:       the current ThrottleGroupMember
  * @is_write:  the type of operation (read/write)
- * @ret:       the next BlockBackend with pending requests, or blk if there is
- *             none.
+ * @ret:       the next ThrottleGroupMember with pending requests, or tgm if
+ *             there is none.
  */
-static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write)
+static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
+                                                bool is_write)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
-    BlockBackend *token, *start;
+    ThrottleState *ts = tgm->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    ThrottleGroupMember *token, *start;
 
     start = token = tg->tokens[is_write];
 
     /* get next bs round in round robin style */
-    token = throttle_group_next_blk(token);
-    while (token != start && !blk_has_pending_reqs(token, is_write)) {
-        token = throttle_group_next_blk(token);
+    token = throttle_group_next_tgm(token);
+    while (token != start && !tgm_has_pending_reqs(token, is_write)) {
+        token = throttle_group_next_tgm(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.
+     * then decide the token is the current tgm because chances are
+     * the current tgm get the current request queued.
      */
-    if (token == start && !blk_has_pending_reqs(token, is_write)) {
-        token = blk;
+    if (token == start && !tgm_has_pending_reqs(token, is_write)) {
+        token = tgm;
     }
 
-    /* Either we return the original BB, or one with pending requests */
-    assert(token == blk || blk_has_pending_reqs(token, is_write));
+    /* Either we return the original TGM, or one with pending requests */
+    assert(token == tgm || tgm_has_pending_reqs(token, is_write));
 
     return token;
 }
 
-/* Check if the next I/O request for a BlockBackend needs to be throttled or
- * not. If there's no timer set in this group, set one and update the token
- * accordingly.
+/* Check if the next I/O request for a ThrottleGroupMember needs to be
+ * throttled or not. If there's no timer set in this group, set one and update
+ * the token accordingly.
  *
  * This assumes that tg->lock is held.
  *
- * @blk:        the current BlockBackend
+ * @tgm:        the current ThrottleGroupMember
  * @is_write:   the type of operation (read/write)
  * @ret:        whether the I/O request needs to be throttled or not
  */
-static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
+static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
+                                          bool is_write)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleState *ts = blkp->throttle_state;
-    ThrottleTimers *tt = &blkp->throttle_timers;
+    ThrottleState *ts = tgm->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    ThrottleTimers *tt = &tgm->throttle_timers;
     bool must_wait;
 
-    if (atomic_read(&blkp->io_limits_disabled)) {
+    if (atomic_read(&tgm->io_limits_disabled)) {
         return false;
     }
 
@@ -251,30 +249,29 @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
 
     must_wait = throttle_schedule_timer(ts, tt, is_write);
 
-    /* If a timer just got armed, set blk as the current token */
+    /* If a timer just got armed, set tgm as the current token */
     if (must_wait) {
-        tg->tokens[is_write] = blk;
+        tg->tokens[is_write] = tgm;
         tg->any_timer_armed[is_write] = true;
     }
 
     return must_wait;
 }
 
-/* Start the next pending I/O request for a BlockBackend.  Return whether
+/* Start the next pending I/O request for a ThrottleGroupMember.  Return whether
  * any request was actually pending.
  *
- * @blk:       the current BlockBackend
+ * @tgm:       the current ThrottleGroupMember
  * @is_write:  the type of operation (read/write)
  */
-static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
+static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm,
                                                          bool is_write)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
     bool ret;
 
-    qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
-    ret = qemu_co_queue_next(&blkp->throttled_reqs[is_write]);
-    qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
+    qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
+    ret = qemu_co_queue_next(&tgm->throttled_reqs[is_write]);
+    qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
 
     return ret;
 }
@@ -283,19 +280,19 @@ static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
  *
  * This assumes that tg->lock is held.
  *
- * @blk:       the current BlockBackend
+ * @tgm:       the current ThrottleGroupMember
  * @is_write:  the type of operation (read/write)
  */
-static void schedule_next_request(BlockBackend *blk, bool is_write)
+static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+    ThrottleState *ts = tgm->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     bool must_wait;
-    BlockBackend *token;
+    ThrottleGroupMember *token;
 
     /* Check if there's any pending request to schedule next */
-    token = next_throttle_token(blk, is_write);
-    if (!blk_has_pending_reqs(token, is_write)) {
+    token = next_throttle_token(tgm, is_write);
+    if (!tgm_has_pending_reqs(token, is_write)) {
         return;
     }
 
@@ -304,12 +301,12 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
 
     /* If it doesn't have to wait, queue it for immediate execution */
     if (!must_wait) {
-        /* Give preference to requests from the current blk */
+        /* Give preference to requests from the current tgm */
         if (qemu_in_coroutine() &&
-            throttle_group_co_restart_queue(blk, is_write)) {
-            token = blk;
+            throttle_group_co_restart_queue(tgm, is_write)) {
+            token = tgm;
         } else {
-            ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
+            ThrottleTimers *tt = &token->throttle_timers;
             int64_t now = qemu_clock_get_ns(tt->clock_type);
             timer_mod(tt->timers[is_write], now);
             tg->any_timer_armed[is_write] = true;
@@ -318,80 +315,80 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
     }
 }
 
-/* Check if an I/O request needs to be throttled, wait and set a timer
- * if necessary, and schedule the next request using a round robin
- * algorithm.
+/* Check if an I/O request needs to be throttled, wait and set a timer if
+ * necessary, and schedule the next request using a round robin algorithm.
  *
- * @blk:       the current BlockBackend
+ * @tgm:       the current ThrottleGroupMember
  * @bytes:     the number of bytes for this I/O
  * @is_write:  the type of operation (read/write)
  */
-void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
+void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
                                                         unsigned int bytes,
                                                         bool is_write)
 {
     bool must_wait;
-    BlockBackend *token;
-
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+    ThrottleGroupMember *token;
+    ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
     qemu_mutex_lock(&tg->lock);
 
     /* First we check if this I/O has to be throttled. */
-    token = next_throttle_token(blk, is_write);
+    token = next_throttle_token(tgm, is_write);
     must_wait = throttle_group_schedule_timer(token, is_write);
 
     /* Wait if there's a timer set or queued requests of this type */
-    if (must_wait || blkp->pending_reqs[is_write]) {
-        blkp->pending_reqs[is_write]++;
+    if (must_wait || tgm->pending_reqs[is_write]) {
+        tgm->pending_reqs[is_write]++;
         qemu_mutex_unlock(&tg->lock);
-        qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
-        qemu_co_queue_wait(&blkp->throttled_reqs[is_write],
-                           &blkp->throttled_reqs_lock);
-        qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
+        qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
+        qemu_co_queue_wait(&tgm->throttled_reqs[is_write],
+                           &tgm->throttled_reqs_lock);
+        qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
         qemu_mutex_lock(&tg->lock);
-        blkp->pending_reqs[is_write]--;
+        tgm->pending_reqs[is_write]--;
     }
 
     /* The I/O will be executed, so do the accounting */
-    throttle_account(blkp->throttle_state, is_write, bytes);
+    throttle_account(tgm->throttle_state, is_write, bytes);
 
     /* Schedule the next request */
-    schedule_next_request(blk, is_write);
+    schedule_next_request(tgm, is_write);
 
     qemu_mutex_unlock(&tg->lock);
 }
 
 typedef struct {
-    BlockBackend *blk;
+    ThrottleGroupMember *tgm;
     bool is_write;
 } RestartData;
 
 static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
 {
     RestartData *data = opaque;
-    BlockBackend *blk = data->blk;
+    ThrottleGroupMember *tgm = data->tgm;
+    ThrottleState *ts = tgm->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     bool is_write = data->is_write;
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
     bool empty_queue;
 
-    empty_queue = !throttle_group_co_restart_queue(blk, is_write);
+    empty_queue = !throttle_group_co_restart_queue(tgm, is_write);
 
     /* If the request queue was empty then we have to take care of
      * scheduling the next one */
     if (empty_queue) {
         qemu_mutex_lock(&tg->lock);
-        schedule_next_request(blk, is_write);
+        schedule_next_request(tgm, is_write);
         qemu_mutex_unlock(&tg->lock);
     }
 }
 
-static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
+static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
 {
+    BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
+            throttle_group_member);
+    BlockBackend *blk = blk_by_public(blkp);
     Coroutine *co;
     RestartData rd = {
-        .blk = blk,
+        .tgm = tgm,
         .is_write = is_write
     };
 
@@ -399,28 +396,24 @@ static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
     aio_co_enter(blk_get_aio_context(blk), co);
 }
 
-void throttle_group_restart_blk(BlockBackend *blk)
+void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-
-    if (blkp->throttle_state) {
-        throttle_group_restart_queue(blk, 0);
-        throttle_group_restart_queue(blk, 1);
+    if (tgm->throttle_state) {
+        throttle_group_restart_queue(tgm, 0);
+        throttle_group_restart_queue(tgm, 1);
     }
 }
 
-/* Update the throttle configuration for a particular group. Similar
- * to throttle_config(), but guarantees atomicity within the
- * throttling group.
+/* Update the throttle configuration for a particular group. Similar to
+ * throttle_config(), but guarantees atomicity within the throttling group.
  *
- * @blk: a BlockBackend that is a member of the group
- * @cfg: the configuration to set
+ * @tgm:    a ThrottleGroupMember that is a member of the group
+ * @cfg:    the configuration to set
  */
-void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
+void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleTimers *tt = &blkp->throttle_timers;
-    ThrottleState *ts = blkp->throttle_state;
+    ThrottleTimers *tt = &tgm->throttle_timers;
+    ThrottleState *ts = tgm->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     qemu_mutex_lock(&tg->lock);
     /* throttle_config() cancels the timers */
@@ -433,28 +426,26 @@ void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
     throttle_config(ts, tt, cfg);
     qemu_mutex_unlock(&tg->lock);
 
-    throttle_group_restart_blk(blk);
+    throttle_group_restart_tgm(tgm);
 }
 
 /* Get the throttle configuration from a particular group. Similar to
- * throttle_get_config(), but guarantees atomicity within the
- * throttling group.
+ * throttle_get_config(), but guarantees atomicity within the throttling group.
  *
- * @blk: a BlockBackend that is a member of the group
- * @cfg: the configuration will be written here
+ * @tgm:    a ThrottleGroupMember that is a member of the group
+ * @cfg:    the configuration will be written here
  */
-void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg)
+void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleState *ts = blkp->throttle_state;
+    ThrottleState *ts = tgm->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     qemu_mutex_lock(&tg->lock);
     throttle_get_config(ts, cfg);
     qemu_mutex_unlock(&tg->lock);
 }
 
-/* ThrottleTimers callback. This wakes up a request that was waiting
- * because it had been throttled.
+/* ThrottleTimers callback. This wakes up a request that was waiting because it
+ * had been throttled.
  *
  * @blk:       the BlockBackend whose request had been throttled
  * @is_write:  the type of operation (read/write)
@@ -462,7 +453,8 @@ void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg)
 static void timer_cb(BlockBackend *blk, bool is_write)
 {
     BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleState *ts = blkp->throttle_state;
+    ThrottleGroupMember *tgm = &blkp->throttle_group_member;
+    ThrottleState *ts = tgm->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
 
     /* The timer has just been fired, so we can update the flag */
@@ -471,7 +463,7 @@ static void timer_cb(BlockBackend *blk, bool is_write)
     qemu_mutex_unlock(&tg->lock);
 
     /* Run the request that was waiting for this timer */
-    throttle_group_restart_queue(blk, is_write);
+    throttle_group_restart_queue(tgm, is_write);
 }
 
 static void read_timer_cb(void *opaque)
@@ -484,17 +476,20 @@ static void write_timer_cb(void *opaque)
     timer_cb(opaque, true);
 }
 
-/* Register a BlockBackend in the throttling group, also initializing its
- * timers and updating its throttle_state pointer to point to it. If a
+/* Register a ThrottleGroupMember from the throttling group, also initializing
+ * its timers and updating its throttle_state pointer to point to it. If a
  * throttling group with that name does not exist yet, it will be created.
  *
- * @blk:       the BlockBackend to insert
+ * @tgm:       the ThrottleGroupMember to insert
  * @groupname: the name of the group
  */
-void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
+void throttle_group_register_tgm(ThrottleGroupMember *tgm,
+                                 const char *groupname)
 {
     int i;
-    BlockBackendPublic *blkp = blk_get_public(blk);
+    BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
+            throttle_group_member);
+    BlockBackend *blk = blk_by_public(blkp);
     ThrottleState *ts = throttle_group_incref(groupname);
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     int clock_type = QEMU_CLOCK_REALTIME;
@@ -504,19 +499,19 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
         clock_type = QEMU_CLOCK_VIRTUAL;
     }
 
-    blkp->throttle_state = ts;
+    tgm->throttle_state = ts;
 
     qemu_mutex_lock(&tg->lock);
-    /* If the ThrottleGroup is new set this BlockBackend as the token */
+    /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
     for (i = 0; i < 2; i++) {
         if (!tg->tokens[i]) {
-            tg->tokens[i] = blk;
+            tg->tokens[i] = tgm;
         }
     }
 
-    QLIST_INSERT_HEAD(&tg->head, blkp, round_robin);
+    QLIST_INSERT_HEAD(&tg->head, tgm, round_robin);
 
-    throttle_timers_init(&blkp->throttle_timers,
+    throttle_timers_init(&tgm->throttle_timers,
                          blk_get_aio_context(blk),
                          clock_type,
                          read_timer_cb,
@@ -526,45 +521,46 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
     qemu_mutex_unlock(&tg->lock);
 }
 
-/* Unregister a BlockBackend from its group, removing it from the list,
+/* Unregister a ThrottleGroupMember from its group, removing it from the list,
  * destroying the timers and setting the throttle_state pointer to NULL.
  *
- * The BlockBackend must not have pending throttled requests, so the caller has
- * to drain them first.
+ * The ThrottleGroupMember must not have pending throttled requests, so the
+ * caller has to drain them first.
  *
  * The group will be destroyed if it's empty after this operation.
  *
- * @blk: the BlockBackend to remove
+ * @tgm the ThrottleGroupMember to remove
  */
-void throttle_group_unregister_blk(BlockBackend *blk)
+void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+    ThrottleState *ts = tgm->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    ThrottleGroupMember *token;
     int i;
 
-    assert(blkp->pending_reqs[0] == 0 && blkp->pending_reqs[1] == 0);
-    assert(qemu_co_queue_empty(&blkp->throttled_reqs[0]));
-    assert(qemu_co_queue_empty(&blkp->throttled_reqs[1]));
+    assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
+    assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
+    assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
 
     qemu_mutex_lock(&tg->lock);
     for (i = 0; i < 2; i++) {
-        if (tg->tokens[i] == blk) {
-            BlockBackend *token = throttle_group_next_blk(blk);
-            /* Take care of the case where this is the last blk in the group */
-            if (token == blk) {
+        if (tg->tokens[i] == tgm) {
+            token = throttle_group_next_tgm(tgm);
+            /* Take care of the case where this is the last tgm in the group */
+            if (token == tgm) {
                 token = NULL;
             }
             tg->tokens[i] = token;
         }
     }
 
-    /* remove the current blk from the list */
-    QLIST_REMOVE(blkp, round_robin);
-    throttle_timers_destroy(&blkp->throttle_timers);
+    /* remove the current tgm from the list */
+    QLIST_REMOVE(tgm, round_robin);
+    throttle_timers_destroy(&tgm->throttle_timers);
     qemu_mutex_unlock(&tg->lock);
 
     throttle_group_unref(&tg->ts);
-    blkp->throttle_state = NULL;
+    tgm->throttle_state = NULL;
 }
 
 static void throttle_groups_init(void)
diff --git a/blockdev.c b/blockdev.c
index f92dcf24bf..794e681cf8 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2696,7 +2696,7 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
     if (throttle_enabled(&cfg)) {
         /* Enable I/O limits if they're not enabled yet, otherwise
          * just update the throttling group. */
-        if (!blk_get_public(blk)->throttle_state) {
+        if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
             blk_io_limits_enable(blk,
                                  arg->has_group ? arg->group :
                                  arg->has_device ? arg->device :
@@ -2706,7 +2706,7 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
         }
         /* Set the new throttling configuration */
         blk_set_io_limits(blk, &cfg);
-    } else if (blk_get_public(blk)->throttle_state) {
+    } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
         /* If all throttling settings are set to 0, disable I/O limits */
         blk_io_limits_disable(blk);
     }
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index d983d34074..487b2da461 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -28,19 +28,20 @@
 #include "qemu/throttle.h"
 #include "block/block_int.h"
 
-const char *throttle_group_get_name(BlockBackend *blk);
+const char *throttle_group_get_name(ThrottleGroupMember *tgm);
 
 ThrottleState *throttle_group_incref(const char *name);
 void throttle_group_unref(ThrottleState *ts);
 
-void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg);
-void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg);
+void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
+void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
 
-void throttle_group_register_blk(BlockBackend *blk, const char *groupname);
-void throttle_group_unregister_blk(BlockBackend *blk);
-void throttle_group_restart_blk(BlockBackend *blk);
+void throttle_group_register_tgm(ThrottleGroupMember *tgm,
+                                const char *groupname);
+void throttle_group_unregister_tgm(ThrottleGroupMember *tgm);
+void throttle_group_restart_tgm(ThrottleGroupMember *tgm);
 
-void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
+void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
                                                         unsigned int bytes,
                                                         bool is_write);
 
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 9109657609..e99cbfc865 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -27,6 +27,7 @@
 
 #include "qemu-common.h"
 #include "qemu/timer.h"
+#include "qemu/coroutine.h"
 
 #define THROTTLE_VALUE_MAX 1000000000000000LL
 
@@ -153,4 +154,29 @@ bool throttle_schedule_timer(ThrottleState *ts,
 
 void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
 
+
+/* The ThrottleGroupMember structure indicates membership in a ThrottleGroup
+ * and holds related data.
+ */
+
+typedef struct ThrottleGroupMember {
+    /* throttled_reqs_lock protects the CoQueues for throttled requests.  */
+    CoMutex      throttled_reqs_lock;
+    CoQueue      throttled_reqs[2];
+
+    /* Nonzero if the I/O limits are currently being ignored; generally
+     * it is zero.  Accessed with atomic operations.
+     */
+    unsigned int io_limits_disabled;
+
+    /* The following fields are protected by the ThrottleGroup lock.
+     * See the ThrottleGroup documentation for details.
+     * throttle_state tells us if I/O limits are configured. */
+    ThrottleState *throttle_state;
+    ThrottleTimers throttle_timers;
+    unsigned       pending_reqs[2];
+    QLIST_ENTRY(ThrottleGroupMember) round_robin;
+
+} ThrottleGroupMember;
+
 #endif
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 999eb2333a..4fec907b7f 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -70,24 +70,10 @@ typedef struct BlockDevOps {
 
 /* This struct is embedded in (the private) BlockBackend struct and contains
  * fields that must be public. This is in particular for QLIST_ENTRY() and
- * friends so that BlockBackends can be kept in lists outside block-backend.c */
+ * friends so that BlockBackends can be kept in lists outside block-backend.c
+ * */
 typedef struct BlockBackendPublic {
-    /* throttled_reqs_lock protects the CoQueues for throttled requests.  */
-    CoMutex      throttled_reqs_lock;
-    CoQueue      throttled_reqs[2];
-
-    /* Nonzero if the I/O limits are currently being ignored; generally
-     * it is zero.  Accessed with atomic operations.
-     */
-    unsigned int io_limits_disabled;
-
-    /* The following fields are protected by the ThrottleGroup lock.
-     * See the ThrottleGroup documentation for details.
-     * throttle_state tells us if I/O limits are configured. */
-    ThrottleState *throttle_state;
-    ThrottleTimers throttle_timers;
-    unsigned       pending_reqs[2];
-    QLIST_ENTRY(BlockBackendPublic) round_robin;
+    ThrottleGroupMember throttle_group_member;
 } BlockBackendPublic;
 
 BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm);
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index a9201b1fea..0f95da2592 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -592,6 +592,7 @@ static void test_groups(void)
     ThrottleConfig cfg1, cfg2;
     BlockBackend *blk1, *blk2, *blk3;
     BlockBackendPublic *blkp1, *blkp2, *blkp3;
+    ThrottleGroupMember *tgm1, *tgm2, *tgm3;
 
     /* No actual I/O is performed on these devices */
     blk1 = blk_new(0, BLK_PERM_ALL);
@@ -602,21 +603,25 @@ static void test_groups(void)
     blkp2 = blk_get_public(blk2);
     blkp3 = blk_get_public(blk3);
 
-    g_assert(blkp1->throttle_state == NULL);
-    g_assert(blkp2->throttle_state == NULL);
-    g_assert(blkp3->throttle_state == NULL);
+    tgm1 = &blkp1->throttle_group_member;
+    tgm2 = &blkp2->throttle_group_member;
+    tgm3 = &blkp3->throttle_group_member;
 
-    throttle_group_register_blk(blk1, "bar");
-    throttle_group_register_blk(blk2, "foo");
-    throttle_group_register_blk(blk3, "bar");
+    g_assert(tgm1->throttle_state == NULL);
+    g_assert(tgm2->throttle_state == NULL);
+    g_assert(tgm3->throttle_state == NULL);
 
-    g_assert(blkp1->throttle_state != NULL);
-    g_assert(blkp2->throttle_state != NULL);
-    g_assert(blkp3->throttle_state != NULL);
+    throttle_group_register_tgm(tgm1, "bar");
+    throttle_group_register_tgm(tgm2, "foo");
+    throttle_group_register_tgm(tgm3, "bar");
 
-    g_assert(!strcmp(throttle_group_get_name(blk1), "bar"));
-    g_assert(!strcmp(throttle_group_get_name(blk2), "foo"));
-    g_assert(blkp1->throttle_state == blkp3->throttle_state);
+    g_assert(tgm1->throttle_state != NULL);
+    g_assert(tgm2->throttle_state != NULL);
+    g_assert(tgm3->throttle_state != NULL);
+
+    g_assert(!strcmp(throttle_group_get_name(tgm1), "bar"));
+    g_assert(!strcmp(throttle_group_get_name(tgm2), "foo"));
+    g_assert(tgm1->throttle_state == tgm3->throttle_state);
 
     /* Setting the config of a group member affects the whole group */
     throttle_config_init(&cfg1);
@@ -624,29 +629,29 @@ static void test_groups(void)
     cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
     cfg1.buckets[THROTTLE_OPS_READ].avg  = 20000;
     cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
-    throttle_group_config(blk1, &cfg1);
+    throttle_group_config(tgm1, &cfg1);
 
-    throttle_group_get_config(blk1, &cfg1);
-    throttle_group_get_config(blk3, &cfg2);
+    throttle_group_get_config(tgm1, &cfg1);
+    throttle_group_get_config(tgm3, &cfg2);
     g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
 
     cfg2.buckets[THROTTLE_BPS_READ].avg  = 4547;
     cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
     cfg2.buckets[THROTTLE_OPS_READ].avg  = 123;
     cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
-    throttle_group_config(blk3, &cfg1);
+    throttle_group_config(tgm3, &cfg1);
 
-    throttle_group_get_config(blk1, &cfg1);
-    throttle_group_get_config(blk3, &cfg2);
+    throttle_group_get_config(tgm1, &cfg1);
+    throttle_group_get_config(tgm3, &cfg2);
     g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
 
-    throttle_group_unregister_blk(blk1);
-    throttle_group_unregister_blk(blk2);
-    throttle_group_unregister_blk(blk3);
+    throttle_group_unregister_tgm(tgm1);
+    throttle_group_unregister_tgm(tgm2);
+    throttle_group_unregister_tgm(tgm3);
 
-    g_assert(blkp1->throttle_state == NULL);
-    g_assert(blkp2->throttle_state == NULL);
-    g_assert(blkp3->throttle_state == NULL);
+    g_assert(tgm1->throttle_state == NULL);
+    g_assert(tgm2->throttle_state == NULL);
+    g_assert(tgm3->throttle_state == NULL);
 }
 
 int main(int argc, char **argv)
-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
@ 2017-06-23 12:46 ` Manos Pitsidianakis
  2017-06-26 13:36   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
                     ` (2 more replies)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver Manos Pitsidianakis
                   ` (6 subsequent siblings)
  8 siblings, 3 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

timer_cb() needs to know about the current Aio context of the throttle
request that is woken up. In order to make ThrottleGroupMember backend
agnostic, this information is stored in an aio_context field instead of
accessing it from BlockBackend.

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/block-backend.c   |  1 +
 block/throttle-groups.c | 19 +++++----------
 include/qemu/throttle.h |  1 +
 tests/test-throttle.c   | 65 +++++++++++++++++++++++++++----------------------
 util/throttle.c         |  4 +++
 5 files changed, 48 insertions(+), 42 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index 90a7abaa53..1d501ec973 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1928,6 +1928,7 @@ void blk_io_limits_disable(BlockBackend *blk)
 /* should be called before blk_set_io_limits if a limit is set */
 void blk_io_limits_enable(BlockBackend *blk, const char *group)
 {
+    blk->public.throttle_group_member.aio_context = blk_get_aio_context(blk);
     assert(!blk->public.throttle_group_member.throttle_state);
     throttle_group_register_tgm(&blk->public.throttle_group_member, group);
 }
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 5e9d8fb4d6..7883cbb511 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -71,6 +71,7 @@ static QemuMutex throttle_groups_lock;
 static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
     QTAILQ_HEAD_INITIALIZER(throttle_groups);
 
+
 /* Increments the reference count of a ThrottleGroup given its name.
  *
  * If no ThrottleGroup is found with the given name a new one is
@@ -383,9 +384,6 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
 
 static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
 {
-    BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
-            throttle_group_member);
-    BlockBackend *blk = blk_by_public(blkp);
     Coroutine *co;
     RestartData rd = {
         .tgm = tgm,
@@ -393,7 +391,7 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write
     };
 
     co = qemu_coroutine_create(throttle_group_restart_queue_entry, &rd);
-    aio_co_enter(blk_get_aio_context(blk), co);
+    aio_co_enter(tgm->aio_context, co);
 }
 
 void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
@@ -447,13 +445,11 @@ void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
 /* ThrottleTimers callback. This wakes up a request that was waiting because it
  * had been throttled.
  *
- * @blk:       the BlockBackend whose request had been throttled
+ * @tgm:       the ThrottleGroupMember whose request had been throttled
  * @is_write:  the type of operation (read/write)
  */
-static void timer_cb(BlockBackend *blk, bool is_write)
+static void timer_cb(ThrottleGroupMember *tgm, bool is_write)
 {
-    BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleGroupMember *tgm = &blkp->throttle_group_member;
     ThrottleState *ts = tgm->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
 
@@ -487,9 +483,6 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
                                  const char *groupname)
 {
     int i;
-    BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
-            throttle_group_member);
-    BlockBackend *blk = blk_by_public(blkp);
     ThrottleState *ts = throttle_group_incref(groupname);
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     int clock_type = QEMU_CLOCK_REALTIME;
@@ -512,11 +505,11 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
     QLIST_INSERT_HEAD(&tg->head, tgm, round_robin);
 
     throttle_timers_init(&tgm->throttle_timers,
-                         blk_get_aio_context(blk),
+                         tgm->aio_context,
                          clock_type,
                          read_timer_cb,
                          write_timer_cb,
-                         blk);
+                         tgm);
 
     qemu_mutex_unlock(&tg->lock);
 }
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index e99cbfc865..3e92d4d4eb 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -160,6 +160,7 @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
  */
 
 typedef struct ThrottleGroupMember {
+    AioContext  *aio_context;
     /* throttled_reqs_lock protects the CoQueues for throttled requests.  */
     CoMutex      throttled_reqs_lock;
     CoQueue      throttled_reqs[2];
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 0f95da2592..d3298234aa 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -24,8 +24,9 @@
 static AioContext     *ctx;
 static LeakyBucket    bkt;
 static ThrottleConfig cfg;
+static ThrottleGroupMember tgm;
 static ThrottleState  ts;
-static ThrottleTimers tt;
+static ThrottleTimers *tt;
 
 /* useful function */
 static bool double_cmp(double x, double y)
@@ -153,19 +154,21 @@ static void test_init(void)
 {
     int i;
 
+    tt = &tgm.throttle_timers;
+
     /* fill the structures with crap */
     memset(&ts, 1, sizeof(ts));
-    memset(&tt, 1, sizeof(tt));
+    memset(tt, 1, sizeof(*tt));
 
     /* init structures */
     throttle_init(&ts);
-    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+    throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
                          read_timer_cb, write_timer_cb, &ts);
 
     /* check initialized fields */
-    g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
-    g_assert(tt.timers[0]);
-    g_assert(tt.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);
@@ -176,18 +179,18 @@ static void test_init(void)
         g_assert(!ts.cfg.buckets[i].level);
     }
 
-    throttle_timers_destroy(&tt);
+    throttle_timers_destroy(tt);
 }
 
 static void test_destroy(void)
 {
     int i;
     throttle_init(&ts);
-    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+    throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
                          read_timer_cb, write_timer_cb, &ts);
-    throttle_timers_destroy(&tt);
+    throttle_timers_destroy(tt);
     for (i = 0; i < 2; i++) {
-        g_assert(!tt.timers[i]);
+        g_assert(!tt->timers[i]);
     }
 }
 
@@ -224,11 +227,11 @@ static void test_config_functions(void)
     orig_cfg.op_size = 1;
 
     throttle_init(&ts);
-    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+    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, &tt, &orig_cfg);
+    throttle_config(&ts, tt, &orig_cfg);
 
     /* has previous leak been initialized by throttle_config ? */
     g_assert(ts.previous_leak);
@@ -236,7 +239,7 @@ static void test_config_functions(void)
     /* get back the fixed configuration */
     throttle_get_config(&ts, &final_cfg);
 
-    throttle_timers_destroy(&tt);
+    throttle_timers_destroy(tt);
 
     g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
     g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg  == 56);
@@ -417,45 +420,45 @@ static void test_have_timer(void)
 {
     /* zero structures */
     memset(&ts, 0, sizeof(ts));
-    memset(&tt, 0, sizeof(tt));
+    memset(tt, 0, sizeof(*tt));
 
     /* no timer set should return false */
-    g_assert(!throttle_timers_are_initialized(&tt));
+    g_assert(!throttle_timers_are_initialized(tt));
 
     /* init structures */
     throttle_init(&ts);
-    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+    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_timers_are_initialized(&tt));
+    g_assert(throttle_timers_are_initialized(tt));
 
-    throttle_timers_destroy(&tt);
+    throttle_timers_destroy(tt);
 }
 
 static void test_detach_attach(void)
 {
     /* zero structures */
     memset(&ts, 0, sizeof(ts));
-    memset(&tt, 0, sizeof(tt));
+    memset(tt, 0, sizeof(*tt));
 
     /* init the structure */
     throttle_init(&ts);
-    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+    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_timers_are_initialized(&tt));
+    g_assert(throttle_timers_are_initialized(tt));
 
     /* timer should no longer exist after detaching */
-    throttle_timers_detach_aio_context(&tt);
-    g_assert(!throttle_timers_are_initialized(&tt));
+    throttle_timers_detach_aio_context(tt);
+    g_assert(!throttle_timers_are_initialized(tt));
 
     /* timer should exist again after attaching */
-    throttle_timers_attach_aio_context(&tt, ctx);
-    g_assert(throttle_timers_are_initialized(&tt));
+    throttle_timers_attach_aio_context(tt, ctx);
+    g_assert(throttle_timers_are_initialized(tt));
 
-    throttle_timers_destroy(&tt);
+    throttle_timers_destroy(tt);
 }
 
 static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
@@ -484,9 +487,9 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
     cfg.op_size = op_size;
 
     throttle_init(&ts);
-    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+    throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
                          read_timer_cb, write_timer_cb, &ts);
-    throttle_config(&ts, &tt, &cfg);
+    throttle_config(&ts, tt, &cfg);
 
     /* account a read */
     throttle_account(&ts, false, size);
@@ -511,7 +514,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
         return false;
     }
 
-    throttle_timers_destroy(&tt);
+    throttle_timers_destroy(tt);
 
     return true;
 }
@@ -607,6 +610,10 @@ static void test_groups(void)
     tgm2 = &blkp2->throttle_group_member;
     tgm3 = &blkp3->throttle_group_member;
 
+    tgm1->aio_context = blk_get_aio_context(blk1);
+    tgm2->aio_context = blk_get_aio_context(blk2);
+    tgm3->aio_context = blk_get_aio_context(blk3);
+
     g_assert(tgm1->throttle_state == NULL);
     g_assert(tgm2->throttle_state == NULL);
     g_assert(tgm3->throttle_state == NULL);
diff --git a/util/throttle.c b/util/throttle.c
index 3570ed25fc..e763474b1a 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -185,6 +185,8 @@ static bool throttle_compute_timer(ThrottleState *ts,
 void throttle_timers_attach_aio_context(ThrottleTimers *tt,
                                         AioContext *new_context)
 {
+    ThrottleGroupMember *tgm = container_of(tt, ThrottleGroupMember, throttle_timers);
+    tgm->aio_context = new_context;
     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,
@@ -242,6 +244,8 @@ static void throttle_timer_destroy(QEMUTimer **timer)
 void throttle_timers_detach_aio_context(ThrottleTimers *tt)
 {
     int i;
+    ThrottleGroupMember *tgm = container_of(tt, ThrottleGroupMember, throttle_timers);
+    tgm->aio_context = NULL;
 
     for (i = 0; i < 2; i++) {
         throttle_timer_destroy(&tt->timers[i]);
-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember Manos Pitsidianakis
@ 2017-06-23 12:46 ` Manos Pitsidianakis
  2017-06-26 14:00   ` [Qemu-devel] [Qemu-block] " Manos Pitsidianakis
                     ` (3 more replies)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
                   ` (5 subsequent siblings)
  8 siblings, 4 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

block/throttle.c uses existing I/O throttle infrastructure inside a
block filter driver. I/O operations are intercepted in the filter's
read/write coroutines, and referred to block/throttle-groups.c

The driver can be used with the command
-drive driver=throttle,file.filename=foo.qcow2,iops-total=...
The configuration flags and semantics are identical to the hardcoded
throttling ones.

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/Makefile.objs             |   1 +
 block/throttle.c                | 427 ++++++++++++++++++++++++++++++++++++++++
 include/qemu/throttle-options.h |  60 ++++--
 3 files changed, 469 insertions(+), 19 deletions(-)
 create mode 100644 block/throttle.c

diff --git a/block/Makefile.objs b/block/Makefile.objs
index ea955302c8..bb811a4d01 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -25,6 +25,7 @@ block-obj-y += accounting.o dirty-bitmap.o
 block-obj-y += write-threshold.o
 block-obj-y += backup.o
 block-obj-$(CONFIG_REPLICATION) += replication.o
+block-obj-y += throttle.o
 
 block-obj-y += crypto.o
 
diff --git a/block/throttle.c b/block/throttle.c
new file mode 100644
index 0000000000..0c17051161
--- /dev/null
+++ b/block/throttle.c
@@ -0,0 +1,427 @@
+/*
+ * QEMU block throttling filter driver infrastructure
+ *
+ * Copyright (c) 2017 Manos Pitsidianakis
+ *
+ * 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/osdep.h"
+#include "block/throttle-groups.h"
+#include "qemu/throttle-options.h"
+#include "qapi/error.h"
+
+
+static QemuOptsList throttle_opts = {
+    .name = "throttle",
+    .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
+    .desc = {
+        {
+            .name = QEMU_OPT_IOPS_TOTAL,
+            .type = QEMU_OPT_NUMBER,
+            .help = "limit total I/O operations per second",
+        },{
+            .name = QEMU_OPT_IOPS_READ,
+            .type = QEMU_OPT_NUMBER,
+            .help = "limit read operations per second",
+        },{
+            .name = QEMU_OPT_IOPS_WRITE,
+            .type = QEMU_OPT_NUMBER,
+            .help = "limit write operations per second",
+        },{
+            .name = QEMU_OPT_BPS_TOTAL,
+            .type = QEMU_OPT_NUMBER,
+            .help = "limit total bytes per second",
+        },{
+            .name = QEMU_OPT_BPS_READ,
+            .type = QEMU_OPT_NUMBER,
+            .help = "limit read bytes per second",
+        },{
+            .name = QEMU_OPT_BPS_WRITE,
+            .type = QEMU_OPT_NUMBER,
+            .help = "limit write bytes per second",
+        },{
+            .name = QEMU_OPT_IOPS_TOTAL_MAX,
+            .type = QEMU_OPT_NUMBER,
+            .help = "I/O operations burst",
+        },{
+            .name = QEMU_OPT_IOPS_READ_MAX,
+            .type = QEMU_OPT_NUMBER,
+            .help = "I/O operations read burst",
+        },{
+            .name = QEMU_OPT_IOPS_WRITE_MAX,
+            .type = QEMU_OPT_NUMBER,
+            .help = "I/O operations write burst",
+        },{
+            .name = QEMU_OPT_BPS_TOTAL_MAX,
+            .type = QEMU_OPT_NUMBER,
+            .help = "total bytes burst",
+        },{
+            .name = QEMU_OPT_BPS_READ_MAX,
+            .type = QEMU_OPT_NUMBER,
+            .help = "total bytes read burst",
+        },{
+            .name = QEMU_OPT_BPS_WRITE_MAX,
+            .type = QEMU_OPT_NUMBER,
+            .help = "total bytes write burst",
+        },{
+            .name = QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
+            .type = QEMU_OPT_NUMBER,
+            .help = "length of the iopstotalmax burst period, in seconds",
+        },{
+            .name = QEMU_OPT_IOPS_READ_MAX_LENGTH,
+            .type = QEMU_OPT_NUMBER,
+            .help = "length of the iopsreadmax burst period, in seconds",
+        },{
+            .name = QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
+            .type = QEMU_OPT_NUMBER,
+            .help = "length of the iopswritemax burst period, in seconds",
+        },{
+            .name = QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
+            .type = QEMU_OPT_NUMBER,
+            .help = "length of the bpstotalmax burst period, in seconds",
+        },{
+            .name = QEMU_OPT_BPS_READ_MAX_LENGTH,
+            .type = QEMU_OPT_NUMBER,
+            .help = "length of the bpsreadmax burst period, in seconds",
+        },{
+            .name = QEMU_OPT_BPS_WRITE_MAX_LENGTH,
+            .type = QEMU_OPT_NUMBER,
+            .help = "length of the bpswritemax burst period, in seconds",
+        },{
+            .name = QEMU_OPT_IOPS_SIZE,
+            .type = QEMU_OPT_NUMBER,
+            .help = "when limiting by iops max size of an I/O in bytes",
+        },
+        {
+            .name = QEMU_OPT_THROTTLE_GROUP_NAME,
+            .type = QEMU_OPT_STRING,
+            .help = "throttle group name",
+        },
+        { /* end of list */ }
+    },
+};
+
+/* Extract ThrottleConfig options. Assumes cfg is initialized and will be
+ * checked for validity.
+ */
+
+static void throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg)
+{
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL)) {
+        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ)) {
+        cfg->buckets[THROTTLE_BPS_READ].avg  =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE)) {
+        cfg->buckets[THROTTLE_BPS_WRITE].avg =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL)) {
+        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ)) {
+        cfg->buckets[THROTTLE_OPS_READ].avg =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE)) {
+        cfg->buckets[THROTTLE_OPS_WRITE].avg =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX)) {
+        cfg->buckets[THROTTLE_BPS_TOTAL].max =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX)) {
+        cfg->buckets[THROTTLE_BPS_READ].max  =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX)) {
+        cfg->buckets[THROTTLE_BPS_WRITE].max =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX)) {
+        cfg->buckets[THROTTLE_OPS_TOTAL].max =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX)) {
+        cfg->buckets[THROTTLE_OPS_READ].max =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX)) {
+        cfg->buckets[THROTTLE_OPS_WRITE].max =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX, 0);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
+        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX_LENGTH)) {
+        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
+        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
+            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
+        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
+        cfg->buckets[THROTTLE_OPS_READ].burst_length =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
+        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_SIZE)) {
+        cfg->op_size =
+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_SIZE, 0);
+    }
+}
+
+static int throttle_configure_tgm(BlockDriverState *bs, ThrottleGroupMember *tgm,
+                                                    QDict *options, Error **errp)
+{
+    int ret = 0;
+    ThrottleState *ts;
+    ThrottleTimers *tt;
+    ThrottleConfig cfg;
+    QemuOpts *opts = NULL;
+    const char *group_name = NULL;
+    Error *local_err = NULL;
+
+    opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return -EINVAL;
+    }
+    qemu_opts_absorb_qdict(opts, options, &local_err);
+    if (local_err) {
+        goto err;
+    }
+
+    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
+    if (!group_name) {
+        group_name = bdrv_get_device_or_node_name(bs);
+        if (!strlen(group_name)) {
+            error_setg(&local_err,
+                       "A group name must be specified for this device.");
+            goto err;
+        }
+    }
+
+    tgm->aio_context = bdrv_get_aio_context(bs);
+    /* Register membership to group with name group_name */
+    throttle_group_register_tgm(tgm, group_name);
+
+    ts = tgm->throttle_state;
+    /* Copy previous configuration */
+    throttle_get_config(ts, &cfg);
+
+    /* Change limits if user has specified them */
+    throttle_extract_options(opts, &cfg);
+    if (!throttle_is_valid(&cfg, &local_err)) {
+        throttle_group_unregister_tgm(tgm);
+        goto err;
+    }
+    tt = &tgm->throttle_timers;
+    /* Update group configuration */
+    throttle_config(ts, tt, &cfg);
+
+    qemu_co_queue_init(&tgm->throttled_reqs[0]);
+    qemu_co_queue_init(&tgm->throttled_reqs[1]);
+
+    goto fin;
+
+err:
+    error_propagate(errp, local_err);
+    ret = -EINVAL;
+fin:
+    qemu_opts_del(opts);
+    return ret;
+}
+
+static int throttle_open(BlockDriverState *bs, QDict *options,
+                            int flags, Error **errp)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    Error *local_err = NULL;
+
+    bs->open_flags = flags;
+    bs->file = bdrv_open_child(NULL, options, "file",
+                                    bs, &child_file, false, &local_err);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return -EINVAL;
+    }
+
+    qdict_flatten(options);
+    return throttle_configure_tgm(bs, tgm, options, errp);
+}
+
+static void throttle_close(BlockDriverState *bs)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    bdrv_drained_begin(bs);
+    throttle_group_unregister_tgm(tgm);
+    bdrv_drained_end(bs);
+    return;
+}
+
+
+static int64_t throttle_getlength(BlockDriverState *bs)
+{
+    return bdrv_getlength(bs->file->bs);
+}
+
+
+static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, uint64_t offset,
+                                            uint64_t bytes, QEMUIOVector *qiov,
+                                            int flags)
+{
+
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_group_co_io_limits_intercept(tgm, bytes, false);
+
+    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
+}
+
+static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs, uint64_t offset,
+                                            uint64_t bytes, QEMUIOVector *qiov,
+                                            int flags)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_group_co_io_limits_intercept(tgm, bytes, true);
+
+    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
+}
+
+static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs,
+        int64_t offset, int bytes, BdrvRequestFlags flags)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_group_co_io_limits_intercept(tgm, bytes, true);
+
+    return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
+}
+
+static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
+        int64_t offset, int bytes)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_group_co_io_limits_intercept(tgm, bytes, true);
+
+    return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
+}
+
+static int throttle_co_flush(BlockDriverState *bs)
+{
+    return bdrv_co_flush(bs->file->bs);
+}
+
+static void throttle_detach_aio_context(BlockDriverState *bs)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_timers_detach_aio_context(&tgm->throttle_timers);
+}
+
+static void throttle_attach_aio_context(BlockDriverState *bs,
+                                    AioContext *new_context)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_timers_attach_aio_context(&tgm->throttle_timers, new_context);
+}
+
+static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
+                                      BlockReopenQueue *queue, Error **errp)
+{
+    ThrottleGroupMember *tgm = NULL;
+
+    assert(reopen_state != NULL);
+    assert(reopen_state->bs != NULL);
+
+    reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
+    tgm = reopen_state->opaque;
+
+    return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
+            errp);
+}
+
+static void throttle_reopen_commit(BDRVReopenState *state)
+{
+    ThrottleGroupMember *tgm = state->bs->opaque;
+    BlockDriverState *bs = state->bs;
+
+    bdrv_drained_begin(bs);
+    throttle_group_unregister_tgm(tgm);
+    g_free(state->bs->opaque);
+    state->bs->opaque = state->opaque;
+    bdrv_drained_end(bs);
+
+    state->opaque = NULL;
+}
+
+static void throttle_reopen_abort(BDRVReopenState *state)
+{
+    ThrottleGroupMember *tgm = state->opaque;
+    throttle_group_unregister_tgm(tgm);
+    g_free(state->opaque);
+    state->opaque = NULL;
+}
+
+static BlockDriver bdrv_throttle = {
+    .format_name                        =   "throttle",
+    .protocol_name                      =   "throttle",
+    .instance_size                      =   sizeof(ThrottleGroupMember),
+
+    .bdrv_file_open                     =   throttle_open,
+    .bdrv_close                         =   throttle_close,
+    .bdrv_co_flush                      =   throttle_co_flush,
+
+    .bdrv_child_perm                    =   bdrv_filter_default_perms,
+
+    .bdrv_getlength                     =   throttle_getlength,
+
+    .bdrv_co_preadv                     =   throttle_co_preadv,
+    .bdrv_co_pwritev                    =   throttle_co_pwritev,
+
+    .bdrv_co_pwrite_zeroes              =   throttle_co_pwrite_zeroes,
+    .bdrv_co_pdiscard                   =   throttle_co_pdiscard,
+
+    .bdrv_recurse_is_first_non_filter   =   bdrv_recurse_is_first_non_filter,
+
+    .bdrv_attach_aio_context            =   throttle_attach_aio_context,
+    .bdrv_detach_aio_context            =   throttle_detach_aio_context,
+
+    .bdrv_reopen_prepare                =   throttle_reopen_prepare,
+    .bdrv_reopen_commit                 =   throttle_reopen_commit,
+    .bdrv_reopen_abort                  =   throttle_reopen_abort,
+
+    .is_filter                          =   true,
+};
+
+static void bdrv_throttle_init(void)
+{
+    bdrv_register(&bdrv_throttle);
+}
+
+block_init(bdrv_throttle_init);
diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
index 3133d1ca40..508ee72625 100644
--- a/include/qemu/throttle-options.h
+++ b/include/qemu/throttle-options.h
@@ -10,81 +10,103 @@
 #ifndef THROTTLE_OPTIONS_H
 #define THROTTLE_OPTIONS_H
 
+#define QEMU_OPT_IOPS_TOTAL "iops-total"
+#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max"
+#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length"
+#define QEMU_OPT_IOPS_READ "iops-read"
+#define QEMU_OPT_IOPS_READ_MAX "iops-read-max"
+#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length"
+#define QEMU_OPT_IOPS_WRITE "iops-write"
+#define QEMU_OPT_IOPS_WRITE_MAX "iops-write-max"
+#define QEMU_OPT_IOPS_WRITE_MAX_LENGTH "iops-write-max-length"
+#define QEMU_OPT_BPS_TOTAL "bps-total"
+#define QEMU_OPT_BPS_TOTAL_MAX "bps-total-max"
+#define QEMU_OPT_BPS_TOTAL_MAX_LENGTH "bps-total-max-length"
+#define QEMU_OPT_BPS_READ "bps-read"
+#define QEMU_OPT_BPS_READ_MAX "bps-read-max"
+#define QEMU_OPT_BPS_READ_MAX_LENGTH "bps-read-max-length"
+#define QEMU_OPT_BPS_WRITE "bps-write"
+#define QEMU_OPT_BPS_WRITE_MAX "bps-write-max"
+#define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length"
+#define QEMU_OPT_IOPS_SIZE "iops-size"
+#define QEMU_OPT_THROTTLE_GROUP_NAME "throttling-group"
+
+#define THROTTLE_OPT_PREFIX "throttling."
 #define THROTTLE_OPTS \
           { \
-            .name = "throttling.iops-total",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,\
             .type = QEMU_OPT_NUMBER,\
             .help = "limit total I/O operations per second",\
         },{ \
-            .name = "throttling.iops-read",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,\
             .type = QEMU_OPT_NUMBER,\
             .help = "limit read operations per second",\
         },{ \
-            .name = "throttling.iops-write",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,\
             .type = QEMU_OPT_NUMBER,\
             .help = "limit write operations per second",\
         },{ \
-            .name = "throttling.bps-total",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,\
             .type = QEMU_OPT_NUMBER,\
             .help = "limit total bytes per second",\
         },{ \
-            .name = "throttling.bps-read",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,\
             .type = QEMU_OPT_NUMBER,\
             .help = "limit read bytes per second",\
         },{ \
-            .name = "throttling.bps-write",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,\
             .type = QEMU_OPT_NUMBER,\
             .help = "limit write bytes per second",\
         },{ \
-            .name = "throttling.iops-total-max",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,\
             .type = QEMU_OPT_NUMBER,\
             .help = "I/O operations burst",\
         },{ \
-            .name = "throttling.iops-read-max",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,\
             .type = QEMU_OPT_NUMBER,\
             .help = "I/O operations read burst",\
         },{ \
-            .name = "throttling.iops-write-max",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,\
             .type = QEMU_OPT_NUMBER,\
             .help = "I/O operations write burst",\
         },{ \
-            .name = "throttling.bps-total-max",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,\
             .type = QEMU_OPT_NUMBER,\
             .help = "total bytes burst",\
         },{ \
-            .name = "throttling.bps-read-max",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,\
             .type = QEMU_OPT_NUMBER,\
             .help = "total bytes read burst",\
         },{ \
-            .name = "throttling.bps-write-max",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,\
             .type = QEMU_OPT_NUMBER,\
             .help = "total bytes write burst",\
         },{ \
-            .name = "throttling.iops-total-max-length",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,\
             .type = QEMU_OPT_NUMBER,\
             .help = "length of the iops-total-max burst period, in seconds",\
         },{ \
-            .name = "throttling.iops-read-max-length",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,\
             .type = QEMU_OPT_NUMBER,\
             .help = "length of the iops-read-max burst period, in seconds",\
         },{ \
-            .name = "throttling.iops-write-max-length",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,\
             .type = QEMU_OPT_NUMBER,\
             .help = "length of the iops-write-max burst period, in seconds",\
         },{ \
-            .name = "throttling.bps-total-max-length",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,\
             .type = QEMU_OPT_NUMBER,\
             .help = "length of the bps-total-max burst period, in seconds",\
         },{ \
-            .name = "throttling.bps-read-max-length",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,\
             .type = QEMU_OPT_NUMBER,\
             .help = "length of the bps-read-max burst period, in seconds",\
         },{ \
-            .name = "throttling.bps-write-max-length",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,\
             .type = QEMU_OPT_NUMBER,\
             .help = "length of the bps-write-max burst period, in seconds",\
         },{ \
-            .name = "throttling.iops-size",\
+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,\
             .type = QEMU_OPT_NUMBER,\
             .help = "when limiting by iops max size of an I/O in bytes",\
         }
-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
                   ` (2 preceding siblings ...)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver Manos Pitsidianakis
@ 2017-06-23 12:46 ` Manos Pitsidianakis
  2017-06-26 14:52   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

ThrottleGroup is converted to an object to allow easy runtime
configuration of throttling filter nodes in the BDS graph using QOM.

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/throttle-groups.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/qemu/throttle.h |   4 +
 2 files changed, 355 insertions(+)

diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 7883cbb511..60079dc8ea 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -25,9 +25,11 @@
 #include "qemu/osdep.h"
 #include "sysemu/block-backend.h"
 #include "block/throttle-groups.h"
+#include "qemu/throttle-options.h"
 #include "qemu/queue.h"
 #include "qemu/thread.h"
 #include "sysemu/qtest.h"
+#include "qapi/error.h"
 
 /* The ThrottleGroup structure (with its ThrottleState) is shared
  * among different ThrottleGroupMembers and it's independent from
@@ -54,6 +56,7 @@
  * that BlockBackend has throttled requests in the queue.
  */
 typedef struct ThrottleGroup {
+    Object parent_obj;
     char *name; /* This is constant during the lifetime of the group */
 
     QemuMutex lock; /* This lock protects the following four fields */
@@ -562,3 +565,351 @@ static void throttle_groups_init(void)
 }
 
 block_init(throttle_groups_init);
+
+
+static bool throttle_group_exists(const char *name)
+{
+    ThrottleGroup *iter;
+    bool ret = false;
+
+    qemu_mutex_lock(&throttle_groups_lock);
+    /* Look for an existing group with that name */
+    QTAILQ_FOREACH(iter, &throttle_groups, list) {
+        if (!strcmp(name, iter->name)) {
+            ret = true;
+            break;
+        }
+    }
+
+    qemu_mutex_unlock(&throttle_groups_lock);
+    return ret;
+}
+
+typedef struct ThrottleGroupClass {
+    /* private */
+    ObjectClass parent_class;
+    /* public */
+} ThrottleGroupClass;
+
+
+#define DOUBLE 0
+#define UINT64 1
+#define UNSIGNED 2
+
+typedef struct {
+    BucketType type;
+    int size; /* field size */
+    ptrdiff_t offset; /* offset in LeakyBucket struct. */
+} ThrottleParamInfo;
+
+static ThrottleParamInfo throttle_iops_total_info = {
+    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
+};
+
+static ThrottleParamInfo throttle_iops_total_max_info = {
+    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
+};
+
+static ThrottleParamInfo throttle_iops_total_max_length_info = {
+    THROTTLE_OPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
+};
+
+static ThrottleParamInfo throttle_iops_read_info = {
+    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
+};
+
+static ThrottleParamInfo throttle_iops_read_max_info = {
+    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, max),
+};
+
+static ThrottleParamInfo throttle_iops_read_max_length_info = {
+    THROTTLE_OPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
+};
+
+static ThrottleParamInfo throttle_iops_write_info = {
+    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
+};
+
+static ThrottleParamInfo throttle_iops_write_max_info = {
+    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
+};
+
+static ThrottleParamInfo throttle_iops_write_max_length_info = {
+    THROTTLE_OPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
+};
+
+static ThrottleParamInfo throttle_bps_total_info = {
+    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
+};
+
+static ThrottleParamInfo throttle_bps_total_max_info = {
+    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
+};
+
+static ThrottleParamInfo throttle_bps_total_max_length_info = {
+    THROTTLE_BPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
+};
+
+static ThrottleParamInfo throttle_bps_read_info = {
+    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
+};
+
+static ThrottleParamInfo throttle_bps_read_max_info = {
+    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, max),
+};
+
+static ThrottleParamInfo throttle_bps_read_max_length_info = {
+    THROTTLE_BPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
+};
+
+static ThrottleParamInfo throttle_bps_write_info = {
+    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
+};
+
+static ThrottleParamInfo throttle_bps_write_max_info = {
+    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
+};
+
+static ThrottleParamInfo throttle_bps_write_max_length_info = {
+    THROTTLE_BPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
+};
+
+static ThrottleParamInfo throttle_iops_size_info = {
+    0, UINT64, offsetof(ThrottleConfig, op_size),
+};
+
+
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
+{
+    char *name = NULL;
+    Error *local_error = NULL;
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+
+    name = object_get_canonical_path_component(OBJECT(obj));
+    if (throttle_group_exists(name)) {
+        error_setg(&local_error, "A throttle group with this name already \
+                                  exists.");
+        goto ret;
+    }
+
+    qemu_mutex_lock(&throttle_groups_lock);
+    tg->name = name;
+    qemu_mutex_init(&tg->lock);
+    QLIST_INIT(&tg->head);
+    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+    tg->refcount++;
+    qemu_mutex_unlock(&throttle_groups_lock);
+
+ret:
+    error_propagate(errp, local_error);
+    return;
+
+}
+
+static void throttle_group_set(Object *obj, Visitor *v, const char * name,
+        void *opaque, Error **errp)
+
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    ThrottleConfig cfg = tg->ts.cfg;
+    Error *local_err = NULL;
+    ThrottleParamInfo *info = opaque;
+    int64_t value;
+
+    visit_type_int64(v, name, &value, &local_err);
+
+    if (local_err) {
+        goto out;
+    }
+    if (value < 0) {
+        error_setg(&local_err, "%s value must be in range [0, %"PRId64"]",
+                "iops-total", INT64_MAX); /* change option string */
+        goto out;
+    }
+
+    switch (info->size) {
+    case UINT64:
+        {
+            uint64_t *field = (void *)&cfg.buckets[info->type] + info->offset;
+            *field = value;
+        }
+        break;
+    case DOUBLE:
+        {
+            double *field = (void *)&cfg.buckets[info->type] + info->offset;
+            *field = value;
+        }
+        break;
+    case UNSIGNED:
+        {
+            unsigned *field = (void *)&cfg.buckets[info->type] + info->offset;
+            *field = value;
+        }
+    }
+
+    tg->ts.cfg = cfg;
+
+out:
+    error_propagate(errp, local_err);
+}
+
+static void throttle_group_get(Object *obj, Visitor *v, const char *name,
+        void *opaque, Error **errp)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    ThrottleConfig cfg = tg->ts.cfg;
+    ThrottleParamInfo *info = opaque;
+    int64_t value;
+
+    switch (info->size) {
+    case UINT64:
+        {
+            uint64_t *field = (void *)&cfg.buckets[info->type] + info->offset;
+            value = *field;
+        }
+        break;
+    case DOUBLE:
+        {
+            double *field = (void *)&cfg.buckets[info->type] + info->offset;
+            value = *field;
+        }
+        break;
+    case UNSIGNED:
+        {
+            unsigned *field = (void *)&cfg.buckets[info->type] + info->offset;
+            value = *field;
+        }
+    }
+
+    visit_type_int64(v, name, &value, errp);
+
+}
+
+static void throttle_group_init(Object *obj)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    throttle_init(&tg->ts);
+}
+
+static void throttle_group_class_init(ObjectClass *klass, void *class_data)
+{
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
+
+    ucc->complete = throttle_group_obj_complete;
+    /* iops limits */
+    object_class_property_add(klass, QEMU_OPT_IOPS_TOTAL, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_total_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_TOTAL_MAX, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_total_max_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_total_max_length_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_READ, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_read_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_READ_MAX, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_read_max_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_READ_MAX_LENGTH, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_read_max_length_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_WRITE, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_write_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_WRITE_MAX, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_write_max_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_IOPS_WRITE_MAX_LENGTH, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_write_max_length_info, &error_abort);
+    /* bps limits */
+    object_class_property_add(klass, QEMU_OPT_BPS_TOTAL, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_total_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_TOTAL_MAX, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_total_max_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_TOTAL_MAX_LENGTH, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_total_max_length_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_READ, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_read_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_READ_MAX, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_read_max_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_READ_MAX_LENGTH, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_read_max_length_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_WRITE, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_write_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_WRITE_MAX, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_write_max_info, &error_abort);
+    object_class_property_add(klass, QEMU_OPT_BPS_WRITE_MAX_LENGTH, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_bps_write_max_length_info, &error_abort);
+    /* rest */
+    object_class_property_add(klass, QEMU_OPT_IOPS_SIZE, "int",
+            throttle_group_get,
+            throttle_group_set,
+            NULL, &throttle_iops_size_info, &error_abort);
+}
+
+
+static void throttle_group_finalize(Object *obj)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+
+    qemu_mutex_lock(&throttle_groups_lock);
+    if (--tg->refcount == 0) {
+        QTAILQ_REMOVE(&throttle_groups, tg, list);
+        qemu_mutex_destroy(&tg->lock);
+        g_free(tg->name);
+        g_free(tg);
+    }
+    qemu_mutex_unlock(&throttle_groups_lock);
+
+}
+
+static const TypeInfo throttle_group_info = {
+   .name = TYPE_THROTTLE_GROUP,
+   .parent = TYPE_OBJECT,
+   .class_init = throttle_group_class_init,
+   .instance_size = sizeof(ThrottleGroup),
+   .instance_init = throttle_group_init,
+   .instance_finalize = throttle_group_finalize,
+   .interfaces = (InterfaceInfo[]) {
+       { TYPE_USER_CREATABLE },
+       { }
+   },
+};
+
+static void throttle_group_register_types(void)
+{
+    qemu_mutex_init(&throttle_groups_lock);
+    type_register_static(&throttle_group_info);
+}
+
+type_init(throttle_group_register_types);
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 3e92d4d4eb..dd56baeb35 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -28,6 +28,8 @@
 #include "qemu-common.h"
 #include "qemu/timer.h"
 #include "qemu/coroutine.h"
+#include "qom/object.h"
+#include "qom/object_interfaces.h"
 
 #define THROTTLE_VALUE_MAX 1000000000000000LL
 
@@ -180,4 +182,6 @@ typedef struct ThrottleGroupMember {
 
 } ThrottleGroupMember;
 
+#define TYPE_THROTTLE_GROUP "throttling-group"
+#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), TYPE_THROTTLE_GROUP)
 #endif
-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
                   ` (3 preceding siblings ...)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
@ 2017-06-23 12:46 ` Manos Pitsidianakis
  2017-06-26 14:55   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
                     ` (3 more replies)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver() Manos Pitsidianakis
                   ` (3 subsequent siblings)
  8 siblings, 4 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

This is needed to configure throttle filter driver nodes with QAPI.

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 qapi/block-core.json | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index f85c2235c7..1d4afafe8c 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2119,7 +2119,7 @@
             'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
             'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
             'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
-            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
+            'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
 
 ##
 # @BlockdevOptionsFile:
@@ -2984,6 +2984,7 @@
       'replication':'BlockdevOptionsReplication',
       'sheepdog':   'BlockdevOptionsSheepdog',
       'ssh':        'BlockdevOptionsSsh',
+      'throttle':   'BlockdevOptionsThrottle',
       'vdi':        'BlockdevOptionsGenericFormat',
       'vhdx':       'BlockdevOptionsGenericFormat',
       'vmdk':       'BlockdevOptionsGenericCOWFormat',
@@ -3723,3 +3724,19 @@
   'data' : { 'parent': 'str',
              '*child': 'str',
              '*node': 'str' } }
+
+##
+# @BlockdevOptionsThrottle:
+#
+# Driver specific block device options for Throttle
+#
+# @throttling-group: the name of the throttling group to use
+#
+# @options:        BlockIOThrottle options
+# Since: 2.9
+##
+{ 'struct': 'BlockdevOptionsThrottle',
+  'data': { 'throttling-group': 'str',
+            'file' : 'BlockdevRef',
+            '*options' : 'BlockIOThrottle'
+             } }
-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver()
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
                   ` (4 preceding siblings ...)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
@ 2017-06-23 12:46 ` Manos Pitsidianakis
  2017-06-26 15:11   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-06-28 13:42   ` [Qemu-devel] " Alberto Garcia
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 7/8] block: remove legacy I/O throttling Manos Pitsidianakis
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

Allow passing a QDict *options parameter to bdrv_new_open_driver() so
that it can be used if a driver needs it upon creation. The previous
behaviour (empty bs->options and bs->explicit_options) remains when
options is NULL.

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block.c               | 13 +++++++++----
 block/commit.c        |  4 ++--
 block/mirror.c        |  2 +-
 block/vvfat.c         |  2 +-
 include/block/block.h |  2 +-
 5 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/block.c b/block.c
index 694396281b..c7d9f8959a 100644
--- a/block.c
+++ b/block.c
@@ -1150,20 +1150,25 @@ free_and_fail:
 }
 
 BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
-                                       int flags, Error **errp)
+                                       int flags, QDict *options, Error **errp)
 {
     BlockDriverState *bs;
     int ret;
 
     bs = bdrv_new();
     bs->open_flags = flags;
-    bs->explicit_options = qdict_new();
-    bs->options = qdict_new();
+    if (options) {
+        bs->explicit_options = qdict_clone_shallow(options);
+        bs->options = qdict_clone_shallow(options);
+    } else {
+        bs->explicit_options = qdict_new();
+        bs->options = qdict_new();
+    }
     bs->opaque = NULL;
 
     update_options_from_flags(bs->options, flags);
 
-    ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
+    ret = bdrv_open_driver(bs, drv, node_name, options, flags, errp);
     if (ret < 0) {
         QDECREF(bs->explicit_options);
         QDECREF(bs->options);
diff --git a/block/commit.c b/block/commit.c
index af6fa68cf3..e855f20d7f 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -338,7 +338,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
     /* Insert commit_top block node above top, so we can block consistent read
      * on the backing chain below it */
     commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, filter_node_name, 0,
-                                         errp);
+                                         NULL, errp);
     if (commit_top_bs == NULL) {
         goto fail;
     }
@@ -486,7 +486,7 @@ int bdrv_commit(BlockDriverState *bs)
     backing_file_bs = backing_bs(bs);
 
     commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR,
-                                         &local_err);
+                                         NULL, &local_err);
     if (commit_top_bs == NULL) {
         error_report_err(local_err);
         goto ro_cleanup;
diff --git a/block/mirror.c b/block/mirror.c
index 19afcc6f1a..3ebe953f18 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1155,7 +1155,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
      * reads on the top, while disabling it in the intermediate nodes, and make
      * the backing chain writable. */
     mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, filter_node_name,
-                                         BDRV_O_RDWR, errp);
+                                         BDRV_O_RDWR, NULL, errp);
     if (mirror_top_bs == NULL) {
         return;
     }
diff --git a/block/vvfat.c b/block/vvfat.c
index 8ab647c0c6..c8e8944947 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -3064,7 +3064,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
 #endif
 
     backing = bdrv_new_open_driver(&vvfat_write_target, NULL, BDRV_O_ALLOW_RDWR,
-                                   &error_abort);
+                                   NULL, &error_abort);
     *(void**) backing->opaque = s;
 
     bdrv_set_backing_hd(s->bs, backing, &error_abort);
diff --git a/include/block/block.h b/include/block/block.h
index a4f09df95a..e7db67c059 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -261,7 +261,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
 BlockDriverState *bdrv_open(const char *filename, const char *reference,
                             QDict *options, int flags, Error **errp);
 BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
-                                       int flags, Error **errp);
+                                       int flags, QDict *options, Error **errp);
 BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
                                     BlockDriverState *bs,
                                     QDict *options, int flags);
-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 7/8] block: remove legacy I/O throttling
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
                   ` (5 preceding siblings ...)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver() Manos Pitsidianakis
@ 2017-06-23 12:46 ` Manos Pitsidianakis
  2017-06-26 15:44   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-06-23 12:47 ` [Qemu-devel] [PATCH RFC v3 8/8] block: add throttle block filter driver interface tests Manos Pitsidianakis
  2017-06-26 15:46 ` [Qemu-devel] [Qemu-block] [PATCH RFC v3 0/8] I/O Throtting block filter driver Stefan Hajnoczi
  8 siblings, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

This commit removes all I/O throttling from block/block-backend.c. In
order to support the existing interface, it is changed to use the
block/throttle.c filter driver.

The throttle filter node that is created by the legacy interface is
stored in a 'throttle_node' field in the BlockBackendPublic of the
device. The legacy throttle node is managed by the legacy interface
completely. More advanced configurations with the filter drive are
possible using the QMP API, but these will be ignored by the legacy
interface.

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/block-backend.c          | 158 ++++++++++++++++++++++++++---------------
 block/qapi.c                   |   8 +--
 block/throttle.c               |   4 ++
 blockdev.c                     |  55 ++++++++++----
 include/sysemu/block-backend.h |   8 +--
 tests/test-throttle.c          |  15 ++--
 util/throttle.c                |   4 --
 7 files changed, 160 insertions(+), 92 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index 1d501ec973..c777943572 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -216,9 +216,6 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
     blk->shared_perm = shared_perm;
     blk_set_enable_write_cache(blk, true);
 
-    qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock);
-    qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
-    qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
     block_acct_init(&blk->stats);
 
     notifier_list_init(&blk->remove_bs_notifiers);
@@ -286,8 +283,8 @@ static void blk_delete(BlockBackend *blk)
     assert(!blk->refcnt);
     assert(!blk->name);
     assert(!blk->dev);
-    if (blk->public.throttle_group_member.throttle_state) {
-        blk_io_limits_disable(blk);
+    if (blk->public.throttle_node) {
+        blk_io_limits_disable(blk, &error_abort);
     }
     if (blk->root) {
         blk_remove_bs(blk);
@@ -597,13 +594,7 @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
  */
 void blk_remove_bs(BlockBackend *blk)
 {
-    ThrottleTimers *tt;
-
     notifier_list_notify(&blk->remove_bs_notifiers, blk);
-    if (blk->public.throttle_group_member.throttle_state) {
-        tt = &blk->public.throttle_group_member.throttle_timers;
-        throttle_timers_detach_aio_context(tt);
-    }
 
     blk_update_root_state(blk);
 
@@ -624,12 +615,6 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
     bdrv_ref(bs);
 
     notifier_list_notify(&blk->insert_bs_notifiers, blk);
-    if (blk->public.throttle_group_member.throttle_state) {
-        throttle_timers_attach_aio_context(
-            &blk->public.throttle_group_member.throttle_timers,
-            bdrv_get_aio_context(bs));
-    }
-
     return 0;
 }
 
@@ -987,13 +972,6 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
     }
 
     bdrv_inc_in_flight(bs);
-
-    /* throttling disk I/O */
-    if (blk->public.throttle_group_member.throttle_state) {
-        throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
-                bytes, false);
-    }
-
     ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags);
     bdrv_dec_in_flight(bs);
     return ret;
@@ -1014,11 +992,6 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
     }
 
     bdrv_inc_in_flight(bs);
-    /* throttling disk I/O */
-    if (blk->public.throttle_group_member.throttle_state) {
-        throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
-                bytes, true);
-    }
 
     if (!blk->enable_write_cache) {
         flags |= BDRV_REQ_FUA;
@@ -1686,18 +1659,9 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
 void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
 {
     BlockDriverState *bs = blk_bs(blk);
-    ThrottleTimers *tt;
 
     if (bs) {
-        if (blk->public.throttle_group_member.throttle_state) {
-            tt = &blk->public.throttle_group_member.throttle_timers;
-            throttle_timers_detach_aio_context(tt);
-        }
         bdrv_set_aio_context(bs, new_context);
-        if (blk->public.throttle_group_member.throttle_state) {
-            tt = &blk->public.throttle_group_member.throttle_timers;
-            throttle_timers_attach_aio_context(tt, new_context);
-        }
     }
 }
 
@@ -1914,45 +1878,115 @@ int blk_commit_all(void)
 /* throttling disk I/O limits */
 void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
 {
-    throttle_group_config(&blk->public.throttle_group_member, cfg);
+    ThrottleGroupMember *tgm;
+
+    assert(blk->public.throttle_node);
+    tgm = blk->public.throttle_node->opaque;
+    throttle_group_config(tgm, cfg);
 }
 
-void blk_io_limits_disable(BlockBackend *blk)
+void blk_io_limits_disable(BlockBackend *blk, Error **errp)
 {
-    assert(blk->public.throttle_group_member.throttle_state);
-    bdrv_drained_begin(blk_bs(blk));
-    throttle_group_unregister_tgm(&blk->public.throttle_group_member);
-    bdrv_drained_end(blk_bs(blk));
+    Error *local_err = NULL;
+    BlockDriverState *bs, *throttle_node;
+
+    throttle_node = blk_get_public(blk)->throttle_node;
+
+    assert(throttle_node && throttle_node->refcnt == 1);
+
+    bs = throttle_node->file->bs;
+    blk_get_public(blk)->throttle_node = NULL;
+
+    /* ref throttle_node's child bs so that it isn't lost when throttle_node is
+     * destroyed */
+    bdrv_ref(bs);
+
+    /* this destroys throttle_node */
+    blk_remove_bs(blk);
+
+    blk_insert_bs(blk, bs, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        blk_insert_bs(blk, bs, NULL);
+    }
+    bdrv_unref(bs);
 }
 
 /* should be called before blk_set_io_limits if a limit is set */
-void blk_io_limits_enable(BlockBackend *blk, const char *group)
+void blk_io_limits_enable(BlockBackend *blk, const char *group,  Error **errp)
 {
-    blk->public.throttle_group_member.aio_context = blk_get_aio_context(blk);
-    assert(!blk->public.throttle_group_member.throttle_state);
-    throttle_group_register_tgm(&blk->public.throttle_group_member, group);
+    BlockDriverState *bs = blk_bs(blk), *throttle_node;
+    Error *local_err = NULL;
+    /*
+     * increase bs refcount so it doesn't get deleted when removed
+     * from the BlockBackend's root
+     * */
+    bdrv_ref(bs);
+    blk_remove_bs(blk);
+
+    QDict *options = qdict_new();
+    qdict_set_default_str(options, "file", bs->node_name);
+    qdict_set_default_str(options, "throttling-group", group);
+    throttle_node = bdrv_new_open_driver(bdrv_find_format("throttle"),
+            NULL, bdrv_get_flags(bs), options, &local_err);
+
+    QDECREF(options);
+    if (local_err) {
+        blk_insert_bs(blk, bs, NULL);
+        bdrv_unref(bs);
+        error_propagate(errp, local_err);
+        return;
+    }
+    /* bs will be throttle_node's child now so unref it*/
+    bdrv_unref(bs);
+
+    blk_insert_bs(blk, throttle_node, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        bdrv_ref(bs);
+        bdrv_unref(throttle_node);
+        blk_insert_bs(blk, bs, NULL);
+        bdrv_unref(bs);
+        return;
+    }
+    bdrv_unref(throttle_node);
+
+    assert(throttle_node->file->bs == bs);
+    assert(throttle_node->refcnt == 1);
+
+    blk_get_public(blk)->throttle_node = throttle_node;
 }
 
-void blk_io_limits_update_group(BlockBackend *blk, const char *group)
+void blk_io_limits_update_group(BlockBackend *blk, const char *group, Error **errp)
 {
+    ThrottleGroupMember *tgm;
+    Error *local_err = NULL;
+
     /* this BB is not part of any group */
-    if (!blk->public.throttle_group_member.throttle_state) {
+    if (!blk->public.throttle_node) {
         return;
     }
 
+    tgm = blk->public.throttle_node->opaque;
+
     /* this BB is a part of the same group than the one we want */
-    if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member),
+    if (!g_strcmp0(throttle_group_get_name(tgm),
                 group)) {
         return;
     }
 
-    /* need to change the group this bs belong to */
-    blk_io_limits_disable(blk);
-    blk_io_limits_enable(blk, group);
+    /* need to change the group this bs belongs to */
+    blk_io_limits_disable(blk, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    blk_io_limits_enable(blk, group, errp);
 }
 
 static void blk_root_drained_begin(BdrvChild *child)
 {
+    ThrottleGroupMember *tgm;
     BlockBackend *blk = child->opaque;
 
     if (++blk->quiesce_counter == 1) {
@@ -1963,19 +1997,25 @@ static void blk_root_drained_begin(BdrvChild *child)
 
     /* Note that blk->root may not be accessible here yet if we are just
      * attaching to a BlockDriverState that is drained. Use child instead. */
-
-    if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) {
-        throttle_group_restart_tgm(&blk->public.throttle_group_member);
+    if (blk->public.throttle_node) {
+        tgm = blk->public.throttle_node->opaque;
+        if (atomic_fetch_inc(&tgm->io_limits_disabled) == 0) {
+            throttle_group_restart_tgm(tgm);
+        }
     }
 }
 
 static void blk_root_drained_end(BdrvChild *child)
 {
+    ThrottleGroupMember *tgm;
     BlockBackend *blk = child->opaque;
     assert(blk->quiesce_counter);
 
-    assert(blk->public.throttle_group_member.io_limits_disabled);
-    atomic_dec(&blk->public.throttle_group_member.io_limits_disabled);
+    if (blk->public.throttle_node) {
+        tgm = blk->public.throttle_node->opaque;
+        assert(tgm->io_limits_disabled);
+        atomic_dec(&tgm->io_limits_disabled);
+    }
 
     if (--blk->quiesce_counter == 0) {
         if (blk->dev_ops && blk->dev_ops->drained_end) {
diff --git a/block/qapi.c b/block/qapi.c
index 70ec5552be..053c3cb8b3 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -67,11 +67,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
     info->backing_file_depth = bdrv_get_backing_file_depth(bs);
     info->detect_zeroes = bs->detect_zeroes;
 
-    if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) {
+    if (blk && blk_get_public(blk)->throttle_node) {
         ThrottleConfig cfg;
-        BlockBackendPublic *blkp = blk_get_public(blk);
+        ThrottleGroupMember *tgm = blk_get_public(blk)->throttle_node->opaque;
 
-        throttle_group_get_config(&blkp->throttle_group_member, &cfg);
+        throttle_group_get_config(tgm, &cfg);
 
         info->bps     = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
         info->bps_rd  = cfg.buckets[THROTTLE_BPS_READ].avg;
@@ -120,7 +120,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
 
         info->has_group = true;
         info->group =
-            g_strdup(throttle_group_get_name(&blkp->throttle_group_member));
+            g_strdup(throttle_group_get_name(tgm));
     }
 
     info->write_threshold = bdrv_write_threshold_get(bs);
diff --git a/block/throttle.c b/block/throttle.c
index 0c17051161..62fa28315a 100644
--- a/block/throttle.c
+++ b/block/throttle.c
@@ -341,6 +341,8 @@ static int throttle_co_flush(BlockDriverState *bs)
 static void throttle_detach_aio_context(BlockDriverState *bs)
 {
     ThrottleGroupMember *tgm = bs->opaque;
+    tgm->aio_context = NULL;
+
     throttle_timers_detach_aio_context(&tgm->throttle_timers);
 }
 
@@ -348,6 +350,8 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
                                     AioContext *new_context)
 {
     ThrottleGroupMember *tgm = bs->opaque;
+    tgm->aio_context = new_context;
+
     throttle_timers_attach_aio_context(&tgm->throttle_timers, new_context);
 }
 
diff --git a/blockdev.c b/blockdev.c
index 794e681cf8..c928ced35a 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -610,7 +610,14 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
         if (!throttling_group) {
             throttling_group = id;
         }
-        blk_io_limits_enable(blk, throttling_group);
+        blk_io_limits_enable(blk, throttling_group, &error);
+        if (error) {
+            error_propagate(errp, error);
+            blk_unref(blk);
+            blk = NULL;
+            goto err_no_bs_opts;
+
+        }
         blk_set_io_limits(blk, &cfg);
     }
 
@@ -2621,6 +2628,9 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
     BlockDriverState *bs;
     BlockBackend *blk;
     AioContext *aio_context;
+    BlockDriverState *throttle_node = NULL;
+    ThrottleGroupMember *tgm;
+    Error *local_err = NULL;
 
     blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
                       arg->has_id ? arg->id : NULL,
@@ -2696,19 +2706,38 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
     if (throttle_enabled(&cfg)) {
         /* Enable I/O limits if they're not enabled yet, otherwise
          * just update the throttling group. */
-        if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
-            blk_io_limits_enable(blk,
-                                 arg->has_group ? arg->group :
-                                 arg->has_device ? arg->device :
-                                 arg->id);
-        } else if (arg->has_group) {
-            blk_io_limits_update_group(blk, arg->group);
+        if (!blk_get_public(blk)->throttle_node) {
+            blk_io_limits_enable(blk, arg->has_group ? arg->group :
+                    arg->has_device ? arg->device : arg->id, &local_err);
+            if (local_err) {
+                error_propagate(errp, local_err);
+                goto out;
+            }
         }
-        /* Set the new throttling configuration */
-        blk_set_io_limits(blk, &cfg);
-    } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
-        /* If all throttling settings are set to 0, disable I/O limits */
-        blk_io_limits_disable(blk);
+
+        if (arg->has_group) {
+            /* move throttle node membership to arg->group */
+            blk_io_limits_update_group(blk, arg->group, &local_err);
+            if (local_err) {
+                error_propagate(errp, local_err);
+                goto out;
+            }
+        }
+
+        throttle_node = blk_get_public(blk)->throttle_node;
+        tgm = throttle_node->opaque;
+        throttle_group_config(tgm, &cfg);
+    } else if (blk_get_public(blk)->throttle_node) {
+        /*
+         * If all throttling settings are set to 0, disable I/O limits
+         * by deleting the legacy throttle node
+         * */
+        blk_io_limits_disable(blk, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            goto out;
+        }
+
     }
 
 out:
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 4fec907b7f..bef8fd53fa 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -73,7 +73,7 @@ typedef struct BlockDevOps {
  * friends so that BlockBackends can be kept in lists outside block-backend.c
  * */
 typedef struct BlockBackendPublic {
-    ThrottleGroupMember throttle_group_member;
+    BlockDriverState *throttle_node;
 } BlockBackendPublic;
 
 BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm);
@@ -221,8 +221,8 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
                                   void *opaque, int ret);
 
 void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg);
-void blk_io_limits_disable(BlockBackend *blk);
-void blk_io_limits_enable(BlockBackend *blk, const char *group);
-void blk_io_limits_update_group(BlockBackend *blk, const char *group);
+void blk_io_limits_disable(BlockBackend *blk, Error **errp);
+void blk_io_limits_enable(BlockBackend *blk, const char *group, Error **errp);
+void blk_io_limits_update_group(BlockBackend *blk, const char *group, Error **errp);
 
 #endif
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index d3298234aa..c5b5492e78 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -594,7 +594,6 @@ static void test_groups(void)
 {
     ThrottleConfig cfg1, cfg2;
     BlockBackend *blk1, *blk2, *blk3;
-    BlockBackendPublic *blkp1, *blkp2, *blkp3;
     ThrottleGroupMember *tgm1, *tgm2, *tgm3;
 
     /* No actual I/O is performed on these devices */
@@ -602,13 +601,9 @@ static void test_groups(void)
     blk2 = blk_new(0, BLK_PERM_ALL);
     blk3 = blk_new(0, BLK_PERM_ALL);
 
-    blkp1 = blk_get_public(blk1);
-    blkp2 = blk_get_public(blk2);
-    blkp3 = blk_get_public(blk3);
-
-    tgm1 = &blkp1->throttle_group_member;
-    tgm2 = &blkp2->throttle_group_member;
-    tgm3 = &blkp3->throttle_group_member;
+    tgm1 = g_new0(ThrottleGroupMember, 1);
+    tgm2 = g_new0(ThrottleGroupMember, 1);
+    tgm3 = g_new0(ThrottleGroupMember, 1);
 
     tgm1->aio_context = blk_get_aio_context(blk1);
     tgm2->aio_context = blk_get_aio_context(blk2);
@@ -659,6 +654,10 @@ static void test_groups(void)
     g_assert(tgm1->throttle_state == NULL);
     g_assert(tgm2->throttle_state == NULL);
     g_assert(tgm3->throttle_state == NULL);
+
+    g_free(tgm1);
+    g_free(tgm2);
+    g_free(tgm3);
 }
 
 int main(int argc, char **argv)
diff --git a/util/throttle.c b/util/throttle.c
index e763474b1a..3570ed25fc 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -185,8 +185,6 @@ static bool throttle_compute_timer(ThrottleState *ts,
 void throttle_timers_attach_aio_context(ThrottleTimers *tt,
                                         AioContext *new_context)
 {
-    ThrottleGroupMember *tgm = container_of(tt, ThrottleGroupMember, throttle_timers);
-    tgm->aio_context = new_context;
     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,
@@ -244,8 +242,6 @@ static void throttle_timer_destroy(QEMUTimer **timer)
 void throttle_timers_detach_aio_context(ThrottleTimers *tt)
 {
     int i;
-    ThrottleGroupMember *tgm = container_of(tt, ThrottleGroupMember, throttle_timers);
-    tgm->aio_context = NULL;
 
     for (i = 0; i < 2; i++) {
         throttle_timer_destroy(&tt->timers[i]);
-- 
2.11.0

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

* [Qemu-devel] [PATCH RFC v3 8/8] block: add throttle block filter driver interface tests
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
                   ` (6 preceding siblings ...)
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 7/8] block: remove legacy I/O throttling Manos Pitsidianakis
@ 2017-06-23 12:47 ` Manos Pitsidianakis
  2017-06-28 11:18   ` Kevin Wolf
  2017-06-26 15:46 ` [Qemu-devel] [Qemu-block] [PATCH RFC v3 0/8] I/O Throtting block filter driver Stefan Hajnoczi
  8 siblings, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-23 12:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf, Alberto Garcia

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 tests/qemu-iotests/184     | 144 +++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/184.out |  31 ++++++++++
 tests/qemu-iotests/group   |   1 +
 3 files changed, 176 insertions(+)
 create mode 100755 tests/qemu-iotests/184
 create mode 100644 tests/qemu-iotests/184.out

diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184
new file mode 100755
index 0000000000..529f9edbec
--- /dev/null
+++ b/tests/qemu-iotests/184
@@ -0,0 +1,144 @@
+#!/bin/bash
+#
+# Test I/O throttle block filter driver interface
+#
+# Copyright (C) 2017 Manos Pitsidianakis
+#
+# 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 of the License, or
+# (at your option) any later version.
+#
+# 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/>.
+#
+
+# creator
+owner=
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+status=1	# failure is the default!
+
+_cleanup()
+{
+    _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw qcow2
+_supported_proto file
+_supported_os Linux
+
+function do_run_qemu()
+{
+    echo Testing: "$@" | _filter_imgfmt
+    $QEMU -nographic -qmp stdio -serial none "$@"
+    echo
+}
+
+function run_qemu()
+{
+    do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\
+                          | _filter_qemu_io | _filter_generated_node_ids
+}
+
+_make_test_img 64M
+test_throttle=$($QEMU_IMG --help|grep throttle)
+[ "$test_throttle" = "" ] && _supported_fmt throttle
+
+throttle="driver=throttle"
+
+echo
+echo "== checking interface =="
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+    "arguments":
+    {
+        "driver" : "$IMGFMT",
+        "node-name" : "disk0",
+        "file" : {
+        "driver" :	"file",
+        "filename": "$TEST_IMG"
+    }
+}
+}
+{ "execute" : "object-add",
+    "arguments" : {
+    "qom-type": "throttling-group",
+    "id" : "foobar",
+    "props" : {
+    "iops-total" : 1000
+}
+    }
+}
+{ "execute": "blockdev-add",
+    "arguments":
+    {
+        "driver" : "throttle",
+        "node-name" : "throttle0",
+        "throttling-group" : "foobar",
+        "file" : "disk0"
+    }
+}
+{ "execute": "query-named-block-nodes" }
+{ "execute" : "query-block" }
+{ "execute": "quit" }
+EOF
+
+echo
+echo "== checking legacy interface =="
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "human-monitor-command",
+    "arguments": { "command-line": "drive_add 0 file=$TEST_IMG,format=$IMGFMT,if=none,id=disk" } }
+    { "execute": "device_add",
+        "arguments": { "driver": "virtio-blk", "drive": "disk",
+        "id": "virtio0" } }
+        { "execute": "block_set_io_throttle",
+            "arguments": {
+            "device": "disk",
+            "group" : "foobar",
+            "iops": 100,
+            "iops_rd": 0,
+            "iops_wr": 0,
+            "bps": 0,
+            "bps_rd": 0,
+            "bps_wr": 0
+        }
+    }
+    { "execute" : "query-block" }
+    { "execute": "block_set_io_throttle",
+        "arguments": {
+        "device": "disk",
+        "group" : "foobar",
+        "iops": 0,
+        "iops_rd": 0,
+        "iops_wr": 0,
+        "bps": 0,
+        "bps_rd": 0,
+        "bps_wr": 0
+    }
+}
+{ "execute" : "query-block" }
+{ "execute": "quit" }
+EOF
+
+echo
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out
new file mode 100644
index 0000000000..ed791c6fbe
--- /dev/null
+++ b/tests/qemu-iotests/184.out
@@ -0,0 +1,31 @@
+QA output created by 184
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+
+== checking interface ==
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": [{"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 67108864, "filename": "json:{\"throttling-group\": \"foobar\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}", "format": "throttle", "actual-size": 200704}, "iops_wr": 0, "ro": false, "node-name": "throttle0", "backing_file_depth": 0, "drv": "throttle", "iops": 0, "bps_wr": 0, "write_threshold": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "cache": {"no-flush": false, "direct": false, "writeback": true}, "file": "json:{\"throttling-group\": \"foobar\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}", "encryption_key_missing": false}, {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 67108864, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 200704, "format-specific": {"type": "!
 qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "refcount-bits": 16, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "node-name": "disk0", "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "write_threshold": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "cache": {"no-flush": false, "direct": false, "writeback": true}, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 197120, "filename": "TEST_DIR/t.qcow2", "format": "file", "actual-size": 200704, "dirty-flag": false}, "iops_wr": 0, "ro": false, "node-name": "NODE_NAME", "backing_file_depth": 0, "drv": "file", "iops": 0, "bps_wr": 0, "write_threshold": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "cache": {"no-flush": false, "direct": false, "writeback": true}, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}]}
+{"return": []}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+== checking legacy interface ==
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": "OK\r\n"}
+{"return": {}}
+{"return": {}}
+{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 67108864, "filename": "", "format": "throttle", "actual-size": 200704}, "iops_wr": 0, "group": "foobar", "ro": false, "node-name": "NODE_NAME", "backing_file_depth": 0, "drv": "throttle", "iops": 100, "bps_wr": 0, "write_threshold": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "cache": {"no-flush": false, "direct": false, "writeback": true}, "file": "", "encryption_key_missing": false}, "type": "unknown"}]}
+{"return": {}}
+{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 67108864, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 200704, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "refcount-bits": 16, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "node-name": "NODE_NAME", "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "write_threshold": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "cache": {"no-flush": false, "direct": false, "writeback": true}, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}]}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index a6acafffd7..0c1f46c7ca 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -175,3 +175,4 @@
 181 rw auto migration
 182 rw auto quick
 183 rw auto migration
+184 rw auto quick
-- 
2.11.0

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
@ 2017-06-26 13:23   ` Stefan Hajnoczi
  2017-06-27 12:08   ` [Qemu-devel] " Alberto Garcia
  1 sibling, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 13:23 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:53PM +0300, Manos Pitsidianakis wrote:
> This commit gathers ThrottleGroup membership details from
> BlockBackendPublic into ThrottleGroupMember and refactors existing code
> to use the structure.

This summarizes the code change but doesn't explain why it's necessary.

I suggest something like this:

  This patch eliminates the 1:1 relationship between BlockBackend and
  throttle group state.  Users will be able to create multiple throttle
  nodes, each with its own throttle group state, in the future.  The
  throttle group state cannot be per-BlockBackend anymore, it must be
  per-throttle node.

> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
>  block/block-backend.c           |  66 +++++----
>  block/qapi.c                    |   8 +-
>  block/throttle-groups.c         | 304 ++++++++++++++++++++--------------------
>  blockdev.c                      |   4 +-
>  include/block/throttle-groups.h |  15 +-
>  include/qemu/throttle.h         |  26 ++++
>  include/sysemu/block-backend.h  |  20 +--
>  tests/test-throttle.c           |  53 +++----
>  8 files changed, 260 insertions(+), 236 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember Manos Pitsidianakis
@ 2017-06-26 13:36   ` Stefan Hajnoczi
  2017-06-26 14:03     ` Manos Pitsidianakis
  2017-06-27 12:39   ` [Qemu-devel] " Alberto Garcia
  2017-06-28 11:27   ` Kevin Wolf
  2 siblings, 1 reply; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 13:36 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:54PM +0300, Manos Pitsidianakis wrote:
> timer_cb() needs to know about the current Aio context of the throttle
> request that is woken up. In order to make ThrottleGroupMember backend
> agnostic, this information is stored in an aio_context field instead of
> accessing it from BlockBackend.
> 
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
>  block/block-backend.c   |  1 +
>  block/throttle-groups.c | 19 +++++----------
>  include/qemu/throttle.h |  1 +
>  tests/test-throttle.c   | 65 +++++++++++++++++++++++++++----------------------
>  util/throttle.c         |  4 +++
>  5 files changed, 48 insertions(+), 42 deletions(-)
> 
> diff --git a/block/block-backend.c b/block/block-backend.c
> index 90a7abaa53..1d501ec973 100644
> --- a/block/block-backend.c
> +++ b/block/block-backend.c
> @@ -1928,6 +1928,7 @@ void blk_io_limits_disable(BlockBackend *blk)
>  /* should be called before blk_set_io_limits if a limit is set */
>  void blk_io_limits_enable(BlockBackend *blk, const char *group)
>  {
> +    blk->public.throttle_group_member.aio_context = blk_get_aio_context(blk);
>      assert(!blk->public.throttle_group_member.throttle_state);
>      throttle_group_register_tgm(&blk->public.throttle_group_member, group);

Or throttle_group_register_tgm() could take an AioContext* argument, I
think that's a little cleaner than modifying throttle_group_member
fields in multiple source files.

> diff --git a/block/throttle-groups.c b/block/throttle-groups.c
> index 5e9d8fb4d6..7883cbb511 100644
> --- a/block/throttle-groups.c
> +++ b/block/throttle-groups.c
> @@ -71,6 +71,7 @@ static QemuMutex throttle_groups_lock;
>  static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
>      QTAILQ_HEAD_INITIALIZER(throttle_groups);
>  
> +
>  /* Increments the reference count of a ThrottleGroup given its name.
>   *
>   * If no ThrottleGroup is found with the given name a new one is

Spurious whitespace change.  Please do not change whitespace in random
places.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver Manos Pitsidianakis
@ 2017-06-26 14:00   ` Manos Pitsidianakis
  2017-06-26 14:30   ` Stefan Hajnoczi
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-26 14:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, qemu-block, Alberto Garcia

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

On Fri, Jun 23, 2017 at 03:46:55PM +0300, Manos Pitsidianakis wrote:
>+static QemuOptsList throttle_opts = {
>+    .name = "throttle",
>+    .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
>+    .desc = {
>+        {
>+            .name = QEMU_OPT_IOPS_TOTAL,

throttle_opts should use THROTTLE_OPTS from throttle-options.h (how many 
times can you say throttle opts in a sentence?), this will be corrected.

>+
>+static int throttle_configure_tgm(BlockDriverState *bs, ThrottleGroupMember *tgm,
>+                                                    QDict *options, Error **errp)
>+{
> ....
>+    /* Copy previous configuration */
>+    throttle_get_config(ts, &cfg);
> ....
>+    /* Update group configuration */
>+    throttle_config(ts, tt, &cfg);

These should be throttle_group_get_config() and throttle_group_config() 
respectively, to use the throttle group locks.

>+
>+block_init(bdrv_throttle_init);
>diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
>index 3133d1ca40..508ee72625 100644
>--- a/include/qemu/throttle-options.h
>+++ b/include/qemu/throttle-options.h
>@@ -10,81 +10,103 @@
> #ifndef THROTTLE_OPTIONS_H
> #define THROTTLE_OPTIONS_H
>
>+#define QEMU_OPT_IOPS_TOTAL "iops-total"
>+#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max"
>+#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length"
>+#define QEMU_OPT_IOPS_READ "iops-read"
>+#define QEMU_OPT_IOPS_READ_MAX "iops-read-max"
>+#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length"
>+#define QEMU_OPT_IOPS_WRITE "iops-write"
>+#define QEMU_OPT_IOPS_WRITE_MAX "iops-write-max"
>+#define QEMU_OPT_IOPS_WRITE_MAX_LENGTH "iops-write-max-length"
>+#define QEMU_OPT_BPS_TOTAL "bps-total"
>+#define QEMU_OPT_BPS_TOTAL_MAX "bps-total-max"
>+#define QEMU_OPT_BPS_TOTAL_MAX_LENGTH "bps-total-max-length"
>+#define QEMU_OPT_BPS_READ "bps-read"
>+#define QEMU_OPT_BPS_READ_MAX "bps-read-max"
>+#define QEMU_OPT_BPS_READ_MAX_LENGTH "bps-read-max-length"
>+#define QEMU_OPT_BPS_WRITE "bps-write"
>+#define QEMU_OPT_BPS_WRITE_MAX "bps-write-max"
>+#define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length"
>+#define QEMU_OPT_IOPS_SIZE "iops-size"
>+#define QEMU_OPT_THROTTLE_GROUP_NAME "throttling-group"
>+
>+#define THROTTLE_OPT_PREFIX "throttling."
> #define THROTTLE_OPTS \
>           { \
>-            .name = "throttling.iops-total",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "limit total I/O operations per second",\
>         },{ \
>-            .name = "throttling.iops-read",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "limit read operations per second",\
>         },{ \
>-            .name = "throttling.iops-write",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "limit write operations per second",\
>         },{ \
>-            .name = "throttling.bps-total",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "limit total bytes per second",\
>         },{ \
>-            .name = "throttling.bps-read",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "limit read bytes per second",\
>         },{ \
>-            .name = "throttling.bps-write",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "limit write bytes per second",\
>         },{ \
>-            .name = "throttling.iops-total-max",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "I/O operations burst",\
>         },{ \
>-            .name = "throttling.iops-read-max",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "I/O operations read burst",\
>         },{ \
>-            .name = "throttling.iops-write-max",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "I/O operations write burst",\
>         },{ \
>-            .name = "throttling.bps-total-max",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "total bytes burst",\
>         },{ \
>-            .name = "throttling.bps-read-max",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "total bytes read burst",\
>         },{ \
>-            .name = "throttling.bps-write-max",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "total bytes write burst",\
>         },{ \
>-            .name = "throttling.iops-total-max-length",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "length of the iops-total-max burst period, in seconds",\
>         },{ \
>-            .name = "throttling.iops-read-max-length",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "length of the iops-read-max burst period, in seconds",\
>         },{ \
>-            .name = "throttling.iops-write-max-length",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "length of the iops-write-max burst period, in seconds",\
>         },{ \
>-            .name = "throttling.bps-total-max-length",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "length of the bps-total-max burst period, in seconds",\
>         },{ \
>-            .name = "throttling.bps-read-max-length",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "length of the bps-read-max burst period, in seconds",\
>         },{ \
>-            .name = "throttling.bps-write-max-length",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "length of the bps-write-max burst period, in seconds",\
>         },{ \
>-            .name = "throttling.iops-size",\
>+            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,\
>             .type = QEMU_OPT_NUMBER,\
>             .help = "when limiting by iops max size of an I/O in bytes",\
>         }
>-- 
>2.11.0
>
>
>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember
  2017-06-26 13:36   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-26 14:03     ` Manos Pitsidianakis
  0 siblings, 0 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-26 14:03 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 02:36:11PM +0100, Stefan Hajnoczi wrote:
>On Fri, Jun 23, 2017 at 03:46:54PM +0300, Manos Pitsidianakis wrote:
>> timer_cb() needs to know about the current Aio context of the throttle
>> request that is woken up. In order to make ThrottleGroupMember backend
>> agnostic, this information is stored in an aio_context field instead of
>> accessing it from BlockBackend.
>>
>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>> ---
>>  block/block-backend.c   |  1 +
>>  block/throttle-groups.c | 19 +++++----------
>>  include/qemu/throttle.h |  1 +
>>  tests/test-throttle.c   | 65 +++++++++++++++++++++++++++----------------------
>>  util/throttle.c         |  4 +++
>>  5 files changed, 48 insertions(+), 42 deletions(-)
>>
>> diff --git a/block/block-backend.c b/block/block-backend.c
>> index 90a7abaa53..1d501ec973 100644
>> --- a/block/block-backend.c
>> +++ b/block/block-backend.c
>> @@ -1928,6 +1928,7 @@ void blk_io_limits_disable(BlockBackend *blk)
>>  /* should be called before blk_set_io_limits if a limit is set */
>>  void blk_io_limits_enable(BlockBackend *blk, const char *group)
>>  {
>> +    blk->public.throttle_group_member.aio_context = blk_get_aio_context(blk);
>>      assert(!blk->public.throttle_group_member.throttle_state);
>>      throttle_group_register_tgm(&blk->public.throttle_group_member, group);
>
>Or throttle_group_register_tgm() could take an AioContext* argument, I
>think that's a little cleaner than modifying throttle_group_member
>fields in multiple source files.

Indeed that is cleaner.

>
>> diff --git a/block/throttle-groups.c b/block/throttle-groups.c
>> index 5e9d8fb4d6..7883cbb511 100644
>> --- a/block/throttle-groups.c
>> +++ b/block/throttle-groups.c
>> @@ -71,6 +71,7 @@ static QemuMutex throttle_groups_lock;
>>  static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
>>      QTAILQ_HEAD_INITIALIZER(throttle_groups);
>>
>> +
>>  /* Increments the reference count of a ThrottleGroup given its name.
>>   *
>>   * If no ThrottleGroup is found with the given name a new one is
>
>Spurious whitespace change.  Please do not change whitespace in random
>places.

I noticed it too late unfortunately.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver Manos Pitsidianakis
  2017-06-26 14:00   ` [Qemu-devel] [Qemu-block] " Manos Pitsidianakis
@ 2017-06-26 14:30   ` Stefan Hajnoczi
  2017-06-26 16:01     ` Manos Pitsidianakis
  2017-06-26 16:26     ` Manos Pitsidianakis
  2017-06-26 14:34   ` Stefan Hajnoczi
  2017-06-28 14:40   ` [Qemu-devel] " Kevin Wolf
  3 siblings, 2 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 14:30 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:55PM +0300, Manos Pitsidianakis wrote:
> +static int throttle_configure_tgm(BlockDriverState *bs, ThrottleGroupMember *tgm,
> +                                                    QDict *options, Error **errp)
> +{
> +    int ret = 0;
> +    ThrottleState *ts;
> +    ThrottleTimers *tt;
> +    ThrottleConfig cfg;
> +    QemuOpts *opts = NULL;
> +    const char *group_name = NULL;
> +    Error *local_err = NULL;
> +
> +    opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return -EINVAL;
> +    }
> +    qemu_opts_absorb_qdict(opts, options, &local_err);
> +    if (local_err) {
> +        goto err;
> +    }
> +
> +    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
> +    if (!group_name) {
> +        group_name = bdrv_get_device_or_node_name(bs);
> +        if (!strlen(group_name)) {
> +            error_setg(&local_err,
> +                       "A group name must be specified for this device.");

To make the error message clearer:
s/A group name/A throttle group name/

> +static int throttle_open(BlockDriverState *bs, QDict *options,
> +                            int flags, Error **errp)
> +{
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    Error *local_err = NULL;
> +
> +    bs->open_flags = flags;

Why is this necessary?  The other block drivers don't do it.  It should
be taken care of in block.c.

> +    bs->file = bdrv_open_child(NULL, options, "file",
> +                                    bs, &child_file, false, &local_err);
> +
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return -EINVAL;
> +    }
> +
> +    qdict_flatten(options);
> +    return throttle_configure_tgm(bs, tgm, options, errp);

Who destroys bs->file on error?

> +}
> +
> +static void throttle_close(BlockDriverState *bs)
> +{
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    bdrv_drained_begin(bs);

Why is this necessary?  bdrv_close() already encloses drv->bdrv_close()
in a bdrv_drained_begin/end region.

> +    throttle_group_unregister_tgm(tgm);
> +    bdrv_drained_end(bs);
> +    return;

Please omit return at the end of a void function.  They are a
distraction.

> +static void throttle_reopen_commit(BDRVReopenState *state)
> +{
> +    ThrottleGroupMember *tgm = state->bs->opaque;
> +    BlockDriverState *bs = state->bs;
> +
> +    bdrv_drained_begin(bs);

Why is this necessary?  bdrv_reopen_multiple() calls
bdrv_reopen_commit() from within a bdrv_drain_all_begin()/end() region.

> +static BlockDriver bdrv_throttle = {
> +    .format_name                        =   "throttle",
> +    .protocol_name                      =   "throttle",
> +    .instance_size                      =   sizeof(ThrottleGroupMember),
> +
> +    .bdrv_file_open                     =   throttle_open,
> +    .bdrv_close                         =   throttle_close,
> +    .bdrv_co_flush                      =   throttle_co_flush,
> +
> +    .bdrv_child_perm                    =   bdrv_filter_default_perms,
> +
> +    .bdrv_getlength                     =   throttle_getlength,
> +
> +    .bdrv_co_preadv                     =   throttle_co_preadv,
> +    .bdrv_co_pwritev                    =   throttle_co_pwritev,
> +
> +    .bdrv_co_pwrite_zeroes              =   throttle_co_pwrite_zeroes,
> +    .bdrv_co_pdiscard                   =   throttle_co_pdiscard,
> +
> +    .bdrv_recurse_is_first_non_filter   =   bdrv_recurse_is_first_non_filter,
> +
> +    .bdrv_attach_aio_context            =   throttle_attach_aio_context,
> +    .bdrv_detach_aio_context            =   throttle_detach_aio_context,
> +
> +    .bdrv_reopen_prepare                =   throttle_reopen_prepare,
> +    .bdrv_reopen_commit                 =   throttle_reopen_commit,
> +    .bdrv_reopen_abort                  =   throttle_reopen_abort,
> +
> +    .is_filter                          =   true,
> +};

Missing:
bdrv_co_get_block_status()
bdrv_truncate()
bdrv_get_info()
bdrv_probe_blocksizes()
bdrv_probe_geometry()
bdrv_media_changed()
bdrv_eject()
bdrv_lock_medium()
bdrv_co_ioctl()

See block/raw-format.c.

I think most of these could be modified in block.c or block/io.c to
automatically call bs->file's function if drv doesn't implement them.
This way all block drivers would transparently pass them through by
default and block/raw-format.c code could be eliminated.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver Manos Pitsidianakis
  2017-06-26 14:00   ` [Qemu-devel] [Qemu-block] " Manos Pitsidianakis
  2017-06-26 14:30   ` Stefan Hajnoczi
@ 2017-06-26 14:34   ` Stefan Hajnoczi
  2017-06-28 14:40   ` [Qemu-devel] " Kevin Wolf
  3 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 14:34 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:55PM +0300, Manos Pitsidianakis wrote:
> +    qemu_co_queue_init(&tgm->throttled_reqs[0]);
> +    qemu_co_queue_init(&tgm->throttled_reqs[1]);

Where is reqs_lock initialized?

It would be cleaner to move the throttled_reqs[] and reqs_lock
initialization into the tgm registration function.  That way it's done
in a single place.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
@ 2017-06-26 14:52   ` Stefan Hajnoczi
  2017-06-26 15:24     ` Manos Pitsidianakis
  2017-06-26 16:58     ` Manos Pitsidianakis
  0 siblings, 2 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 14:52 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:56PM +0300, Manos Pitsidianakis wrote:
> +static bool throttle_group_exists(const char *name)
> +{
> +    ThrottleGroup *iter;
> +    bool ret = false;
> +
> +    qemu_mutex_lock(&throttle_groups_lock);

Not sure if this lock or the throttle_groups list are necessary.

Have you seen iothread.c:qmp_query_iothreads()?  All objects are put
into a container (the parent object), so you can just iterate over its
children.  There's no need for a separate list because QOM already has
all the objects.

> +typedef struct ThrottleGroupClass {
> +    /* private */
> +    ObjectClass parent_class;
> +    /* public */
> +} ThrottleGroupClass;

This is unused?

> +
> +
> +#define DOUBLE 0
> +#define UINT64 1
> +#define UNSIGNED 2
> +
> +typedef struct {
> +    BucketType type;
> +    int size; /* field size */

sizeof(double) == sizeof(uint64_t) == 8.

This is a datatype field, not a size.

> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
> +{
> +    char *name = NULL;
> +    Error *local_error = NULL;
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +
> +    name = object_get_canonical_path_component(OBJECT(obj));
> +    if (throttle_group_exists(name)) {
> +        error_setg(&local_error, "A throttle group with this name already \
> +                                  exists.");
> +        goto ret;
> +    }

QOM should enforce unique id=<ID>.  I don't think this is necessary.

> +
> +    qemu_mutex_lock(&throttle_groups_lock);
> +    tg->name = name;
> +    qemu_mutex_init(&tg->lock);
> +    QLIST_INIT(&tg->head);
> +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> +    tg->refcount++;
> +    qemu_mutex_unlock(&throttle_groups_lock);
> +
> +ret:
> +    error_propagate(errp, local_error);
> +    return;
> +
> +}
> +
> +static void throttle_group_set(Object *obj, Visitor *v, const char * name,
> +        void *opaque, Error **errp)
> +
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg = tg->ts.cfg;
> +    Error *local_err = NULL;
> +    ThrottleParamInfo *info = opaque;
> +    int64_t value;

What happens if this property is set while QEMU is already running?

> +
> +    visit_type_int64(v, name, &value, &local_err);
> +
> +    if (local_err) {
> +        goto out;
> +    }
> +    if (value < 0) {
> +        error_setg(&local_err, "%s value must be in range [0, %"PRId64"]",
> +                "iops-total", INT64_MAX); /* change option string */

iops-total? :)

> +        goto out;
> +    }

This doesn't validate inputs properly for non int64_t types.

I'm also worried that the command-line bps=,iops=,... options do not
have unsigned or double types.  Input ranges and validation should match
the QEMU command-line (I know this is a bit of a pain with QOM since the
property types are different from QEMU option types).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
@ 2017-06-26 14:55   ` Stefan Hajnoczi
  2017-06-27 13:12   ` [Qemu-devel] " Eric Blake
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 14:55 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:57PM +0300, Manos Pitsidianakis wrote:
> This is needed to configure throttle filter driver nodes with QAPI.
> 
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
>  qapi/block-core.json | 19 ++++++++++++++++++-
>  1 file changed, 18 insertions(+), 1 deletion(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver()
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver() Manos Pitsidianakis
@ 2017-06-26 15:11   ` Stefan Hajnoczi
  2017-06-28 15:55     ` Kevin Wolf
  2017-06-28 13:42   ` [Qemu-devel] " Alberto Garcia
  1 sibling, 1 reply; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 15:11 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:58PM +0300, Manos Pitsidianakis wrote:
> diff --git a/block.c b/block.c
> index 694396281b..c7d9f8959a 100644
> --- a/block.c
> +++ b/block.c
> @@ -1150,20 +1150,25 @@ free_and_fail:
>  }
>  
>  BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
> -                                       int flags, Error **errp)
> +                                       int flags, QDict *options, Error **errp)

Please add a doc comment that explains the QDict ownership when options
!= NULL.  Users need to understand whether the options QDict still
belongs to them after the call or bdrv_new_open_driver() takes over
ownership.

See bdrv_open_inherit() for an example.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-26 14:52   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-26 15:24     ` Manos Pitsidianakis
  2017-06-27 12:57       ` Stefan Hajnoczi
  2017-06-26 16:58     ` Manos Pitsidianakis
  1 sibling, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-26 15:24 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 03:52:34PM +0100, Stefan Hajnoczi wrote:
>Have you seen iothread.c:qmp_query_iothreads()?  All objects are put
>into a container (the parent object), so you can just iterate over its
>children.  There's no need for a separate list because QOM already has
>all the objects.

Thanks, QOM was difficult to figure out in general.

>> +
>> +
>> +#define DOUBLE 0
>> +#define UINT64 1
>> +#define UNSIGNED 2
>> +
>> +typedef struct {
>> +    BucketType type;
>> +    int size; /* field size */
>
>sizeof(double) == sizeof(uint64_t) == 8.
>
>This is a datatype field, not a size.
>
>> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
>> +{
>> +    char *name = NULL;
>> +    Error *local_error = NULL;
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +
>> +    name = object_get_canonical_path_component(OBJECT(obj));
>> +    if (throttle_group_exists(name)) {
>> +        error_setg(&local_error, "A throttle group with this name already \
>> +                                  exists.");
>> +        goto ret;
>> +    }
>
>QOM should enforce unique id=<ID>.  I don't think this is necessary.
>
>> +
>> +    qemu_mutex_lock(&throttle_groups_lock);
>> +    tg->name = name;
>> +    qemu_mutex_init(&tg->lock);
>> +    QLIST_INIT(&tg->head);
>> +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
>> +    tg->refcount++;
>> +    qemu_mutex_unlock(&throttle_groups_lock);
>> +
>> +ret:
>> +    error_propagate(errp, local_error);
>> +    return;
>> +
>> +}
>> +
>> +static void throttle_group_set(Object *obj, Visitor *v, const char * name,
>> +        void *opaque, Error **errp)
>> +
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +    ThrottleConfig cfg = tg->ts.cfg;
>> +    Error *local_err = NULL;
>> +    ThrottleParamInfo *info = opaque;
>> +    int64_t value;
>
>What happens if this property is set while QEMU is already running?

I assume you mean setting a property while a group has active members 
and requests? My best answer would be "don't do that". I couldn't figure 
a way to do this cleanly. Individual limit changes may render a 
ThrottleConfig invalid, so it should not be allowed. ThrottleGroups and 
throttle nodes should be destroyed and recreated to change limits with 
this version, but in the next it will be done through 
block_set_io_throttle() which is the existing way to change limits and 
check for validity. This was discussed in the proposal about the new 
syntax we had on the list.

As we also talked about on IRC, clock_type should be a ThrottleGroup 
field in order to set a group's configuration without passing a 
ThrottleGroupMember, so changing this will make group configuring 
possible on QMP.
>
>> +
>> +    visit_type_int64(v, name, &value, &local_err);
>> +
>> +    if (local_err) {
>> +        goto out;
>> +    }
>> +    if (value < 0) {
>> +        error_setg(&local_err, "%s value must be in range [0, %"PRId64"]",
>> +                "iops-total", INT64_MAX); /* change option string */
>
>iops-total? :)

I even left a comment to remind myself to change this... pah.

>
>> +        goto out;
>> +    }
>
>This doesn't validate inputs properly for non int64_t types.
>
>I'm also worried that the command-line bps=,iops=,... options do not
>have unsigned or double types.  Input ranges and validation should match
>the QEMU command-line (I know this is a bit of a pain with QOM since the
>property types are different from QEMU option types).

I don't know what should be done about this, to be honest, except for 
manually checking the limits for each datatype in the QOM setters.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 7/8] block: remove legacy I/O throttling
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 7/8] block: remove legacy I/O throttling Manos Pitsidianakis
@ 2017-06-26 15:44   ` Stefan Hajnoczi
  2017-06-26 22:45     ` Manos Pitsidianakis
  0 siblings, 1 reply; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 15:44 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:59PM +0300, Manos Pitsidianakis wrote:
> @@ -1914,45 +1878,115 @@ int blk_commit_all(void)
>  /* throttling disk I/O limits */
>  void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
>  {
> -    throttle_group_config(&blk->public.throttle_group_member, cfg);
> +    ThrottleGroupMember *tgm;
> +
> +    assert(blk->public.throttle_node);
> +    tgm = blk->public.throttle_node->opaque;
> +    throttle_group_config(tgm, cfg);

block-backend.c should not access ->opaque.  Instead block/throttle.c
could provide an interface:

  void throttle_node_set_config(BlockDriverState *bs,
                                ThrottleConfig *cfg);

We know bs is always a throttle node but it's also possible for
block/trottle.c to check that:

  assert(bs->drv == &throttle_driver_ops);

>  }
>  
> -void blk_io_limits_disable(BlockBackend *blk)
> +void blk_io_limits_disable(BlockBackend *blk, Error **errp)
>  {
> -    assert(blk->public.throttle_group_member.throttle_state);
> -    bdrv_drained_begin(blk_bs(blk));
> -    throttle_group_unregister_tgm(&blk->public.throttle_group_member);
> -    bdrv_drained_end(blk_bs(blk));
> +    Error *local_err = NULL;
> +    BlockDriverState *bs, *throttle_node;
> +
> +    throttle_node = blk_get_public(blk)->throttle_node;
> +
> +    assert(throttle_node && throttle_node->refcnt == 1);

I'm not sure if we can enforce refcnt == 1.  What stops other graph
manipulation operations from inserting a node above or a BB that uses
throttle_node as the root?

> +
> +    bs = throttle_node->file->bs;
> +    blk_get_public(blk)->throttle_node = NULL;

Missing drained_begin/end region around code that modifies the graph.

> +
> +    /* ref throttle_node's child bs so that it isn't lost when throttle_node is
> +     * destroyed */
> +    bdrv_ref(bs);
> +
> +    /* this destroys throttle_node */
> +    blk_remove_bs(blk);

This assumes that throttle_node is the top node.  How is this constraint enforced?

> +
> +    blk_insert_bs(blk, bs, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        blk_insert_bs(blk, bs, NULL);

How does this handle the error? :)

If there's no way to handle the error then error_abort should be used.

> +    }
> +    bdrv_unref(bs);
>  }
>  
>  /* should be called before blk_set_io_limits if a limit is set */
> -void blk_io_limits_enable(BlockBackend *blk, const char *group)
> +void blk_io_limits_enable(BlockBackend *blk, const char *group,  Error **errp)
>  {
> -    blk->public.throttle_group_member.aio_context = blk_get_aio_context(blk);
> -    assert(!blk->public.throttle_group_member.throttle_state);
> -    throttle_group_register_tgm(&blk->public.throttle_group_member, group);

It would be nice to do:

assert(!blk->public.throttle_node);

> +    BlockDriverState *bs = blk_bs(blk), *throttle_node;
> +    Error *local_err = NULL;
> +    /*
> +     * increase bs refcount so it doesn't get deleted when removed
> +     * from the BlockBackend's root
> +     * */
> +    bdrv_ref(bs);
> +    blk_remove_bs(blk);
> +
> +    QDict *options = qdict_new();
> +    qdict_set_default_str(options, "file", bs->node_name);
> +    qdict_set_default_str(options, "throttling-group", group);
> +    throttle_node = bdrv_new_open_driver(bdrv_find_format("throttle"),
> +            NULL, bdrv_get_flags(bs), options, &local_err);
> +
> +    QDECREF(options);

Perhaps it's more consistent to use bdrv_open_inherit() ownership
semantics instead.  Then callers don't need to worry about freeing
options.

> +    if (local_err) {
> +        blk_insert_bs(blk, bs, NULL);

&error_abort

> +        bdrv_unref(bs);
> +        error_propagate(errp, local_err);
> +        return;
> +    }
> +    /* bs will be throttle_node's child now so unref it*/
> +    bdrv_unref(bs);
> +
> +    blk_insert_bs(blk, throttle_node, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);

The only blk_insert_bs() errors are permission errors.  Can the code
guarantee that permissions will always be usable?  Then you can drop the
error handling and just use &error_abort.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 0/8] I/O Throtting block filter driver
  2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
                   ` (7 preceding siblings ...)
  2017-06-23 12:47 ` [Qemu-devel] [PATCH RFC v3 8/8] block: add throttle block filter driver interface tests Manos Pitsidianakis
@ 2017-06-26 15:46 ` Stefan Hajnoczi
  8 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-26 15:46 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Fri, Jun 23, 2017 at 03:46:52PM +0300, Manos Pitsidianakis wrote:
> This patch series adds a filter driver that uses the existing I/O throttle
> code. Throttle groups can be then created and managed with QOM.
> 
> The 'remove legacy I/O throttling' patch replaces existing interface (command
> line, qmp, hmp) to use the throttle filter transparently so as to not break
> backwards compatibility. The created throttle node is stored in a field in
> BlockBackendPublic so that the legacy interface can access it. The patch
> currently breaks test 129 which commits, mirrors and backups a device with
> legacy throttling options. I assume the internal throttling node affects this,
> so I'd like some advice on how to approach this.
> 
> Test 139 could be updated to include the throttle driver though I'm not sure if
> it's needed.
> 
> I'm also not sure about the test I created for this (last patch).
> 
> v2: 
> - Move throttle group membership to ThrottleGroupMember struct
> v3:
> - add ThrottleGroup object
> - convert legacy interface to use the driver
> - add tests
> 
> 
> Manos Pitsidianakis (8):
>   block: move ThrottleGroup membership to ThrottleGroupMember
>   block: Add aio_context field in ThrottleGroupMember
>   block: add throttle block filter driver
>   block: convert ThrottleGroup to object with QOM
>   block: add BlockDevOptionsThrottle to QAPI
>   block: add options parameter to bdrv_new_open_driver()
>   block: remove legacy I/O throttling
>   block: add throttle block filter driver interface tests

Can BlockBackendPublic be removed now?  Since throttling only knows
about ThrottleGroupMember there may be no need for BlockBackendPublic
as a public struct anymore.  It's fields can be inlined inside
BlockBackend.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-26 14:30   ` Stefan Hajnoczi
@ 2017-06-26 16:01     ` Manos Pitsidianakis
  2017-06-27 12:42       ` Stefan Hajnoczi
  2017-06-26 16:26     ` Manos Pitsidianakis
  1 sibling, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-26 16:01 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 03:30:55PM +0100, Stefan Hajnoczi wrote:
>> +static BlockDriver bdrv_throttle = {
>> +    .format_name                        =   "throttle",
>> +    .protocol_name                      =   "throttle",
>> +    .instance_size                      =   sizeof(ThrottleGroupMember),
>> +
>> +    .bdrv_file_open                     =   throttle_open,
>> +    .bdrv_close                         =   throttle_close,
>> +    .bdrv_co_flush                      =   throttle_co_flush,
>> +
>> +    .bdrv_child_perm                    =   bdrv_filter_default_perms,
>> +
>> +    .bdrv_getlength                     =   throttle_getlength,
>> +
>> +    .bdrv_co_preadv                     =   throttle_co_preadv,
>> +    .bdrv_co_pwritev                    =   throttle_co_pwritev,
>> +
>> +    .bdrv_co_pwrite_zeroes              =   throttle_co_pwrite_zeroes,
>> +    .bdrv_co_pdiscard                   =   throttle_co_pdiscard,
>> +
>> +    .bdrv_recurse_is_first_non_filter   =   bdrv_recurse_is_first_non_filter,
>> +
>> +    .bdrv_attach_aio_context            =   throttle_attach_aio_context,
>> +    .bdrv_detach_aio_context            =   throttle_detach_aio_context,
>> +
>> +    .bdrv_reopen_prepare                =   throttle_reopen_prepare,
>> +    .bdrv_reopen_commit                 =   throttle_reopen_commit,
>> +    .bdrv_reopen_abort                  =   throttle_reopen_abort,
>> +
>> +    .is_filter                          =   true,
>> +};
>
>Missing:
>bdrv_co_get_block_status()
>bdrv_truncate()
>bdrv_get_info()
>bdrv_probe_blocksizes()
>bdrv_probe_geometry()
>bdrv_media_changed()
>bdrv_eject()
>bdrv_lock_medium()
>bdrv_co_ioctl()
>
>See block/raw-format.c.
>
>I think most of these could be modified in block.c or block/io.c to
>automatically call bs->file's function if drv doesn't implement them.
>This way all block drivers would transparently pass them through by
>default and block/raw-format.c code could be eliminated.

Are these truly necessary? Because other filter drivers (ie quorum, 
blkverify) don't implement them.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-26 14:30   ` Stefan Hajnoczi
  2017-06-26 16:01     ` Manos Pitsidianakis
@ 2017-06-26 16:26     ` Manos Pitsidianakis
  2017-06-27 12:45       ` Stefan Hajnoczi
  1 sibling, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-26 16:26 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 03:30:55PM +0100, Stefan Hajnoczi wrote:
>> +    bs->file = bdrv_open_child(NULL, options, "file",
>> +                                    bs, &child_file, false, &local_err);
>> +
>> +    if (local_err) {
>> +        error_propagate(errp, local_err);
>> +        return -EINVAL;
>> +    }
>> +
>> +    qdict_flatten(options);
>> +    return throttle_configure_tgm(bs, tgm, options, errp);
>
>Who destroys bs->file on error?

It is reaped by bdrv_open_inherit() on failure, if I'm not mistaken.
That's how other drivers handle this as well. Some (eg block/qcow2.c)
check if bs->file is NULL instead of the error pointer they pass, so
this is not not very consistent.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-26 14:52   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-06-26 15:24     ` Manos Pitsidianakis
@ 2017-06-26 16:58     ` Manos Pitsidianakis
  2017-06-27 13:02       ` Stefan Hajnoczi
  2017-06-27 16:05       ` Alberto Garcia
  1 sibling, 2 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-26 16:58 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 03:52:34PM +0100, Stefan Hajnoczi wrote:
>On Fri, Jun 23, 2017 at 03:46:56PM +0300, Manos Pitsidianakis wrote:
>> +static bool throttle_group_exists(const char *name)
>> +{
>> +    ThrottleGroup *iter;
>> +    bool ret = false;
>> +
>> +    qemu_mutex_lock(&throttle_groups_lock);
>
>Not sure if this lock or the throttle_groups list are necessary.
>
>Have you seen iothread.c:qmp_query_iothreads()?  All objects are put
>into a container (the parent object), so you can just iterate over its
>children.  There's no need for a separate list because QOM already has
>all the objects.
>

>> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
>> +{
>> +    char *name = NULL;
>> +    Error *local_error = NULL;
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +
>> +    name = object_get_canonical_path_component(OBJECT(obj));
>> +    if (throttle_group_exists(name)) {
>> +        error_setg(&local_error, "A throttle group with this name already \
>> +                                  exists.");
>> +        goto ret;
>> +    }
>
>QOM should enforce unique id=<ID>.  I don't think this is necessary.
>
>> +
>> +    qemu_mutex_lock(&throttle_groups_lock);
>> +    tg->name = name;
>> +    qemu_mutex_init(&tg->lock);
>> +    QLIST_INIT(&tg->head);
>> +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
>> +    tg->refcount++;
>> +    qemu_mutex_unlock(&throttle_groups_lock);
>> +

Sorry for the multiple replies but I just remembered this.

This is necessary because throttle groups are created by other 
interfaces as well. Of course block/throttle-groups.c could use only QOM 
objects internally to eliminate the housekeeping.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 7/8] block: remove legacy I/O throttling
  2017-06-26 15:44   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-26 22:45     ` Manos Pitsidianakis
  2017-06-27 13:08       ` Stefan Hajnoczi
  0 siblings, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-26 22:45 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 04:44:44PM +0100, Stefan Hajnoczi wrote:
>On Fri, Jun 23, 2017 at 03:46:59PM +0300, Manos Pitsidianakis wrote:
>> -void blk_io_limits_disable(BlockBackend *blk)
>> +void blk_io_limits_disable(BlockBackend *blk, Error **errp)
>>  {
>> -    assert(blk->public.throttle_group_member.throttle_state);
>> -    bdrv_drained_begin(blk_bs(blk));
>> -    throttle_group_unregister_tgm(&blk->public.throttle_group_member);
>> -    bdrv_drained_end(blk_bs(blk));
>> +    Error *local_err = NULL;
>> +    BlockDriverState *bs, *throttle_node;
>> +
>> +    throttle_node = blk_get_public(blk)->throttle_node;
>> +
>> +    assert(throttle_node && throttle_node->refcnt == 1);
>
>I'm not sure if we can enforce refcnt == 1.  What stops other graph
>manipulation operations from inserting a node above or a BB that uses
>throttle_node as the root?

Is this possible unless the user explicitly does this? I suppose going 
down the path and removing it from any place is okay. If the 
throttle_node has more than one parent then the result would be invalid 
anyway. I don't see anything in the code doing this (removing a BDS from 
a BB->leaf path) or wrapping it in some way, is that what should be 
done?

>
>> +
>> +    /* ref throttle_node's child bs so that it isn't lost when throttle_node is
>> +     * destroyed */
>> +    bdrv_ref(bs);
>> +
>> +    /* this destroys throttle_node */
>> +    blk_remove_bs(blk);
>
>This assumes that throttle_node is the top node.  How is this 
>constraint enforced?


Kevin had said in a previous discussion:
> The throttle node affects only the backing file now, but not the new
> top-level node. With manually created filter nodes we may need to
> provide a way so that the QMP command tells where in the tree the
> snapshot is actually taken. With automatically created ones, we need to
> make sure that they always stay on top.
> 
> Similar problems arise when block jobs manipulate the graph. For
> example, think of a commit block job, which will remove the topmost node
> from the backing file chain. We don't want it to remove the throttling
> filter together with the qcow2 node. (Or do we? It might be something
> that needs to be configurable.)
> 
> We also have several QMP that currently default to working on the
> topmost image of the chain. Some of them may actually want to work on
> the topmost node that isn't an automatically inserted filter.
> 
> 
> I hope this helps you understand why I keep saying that automatic
> insertion/removal of filter nodes is tricky and we should do it as
> little as we possibly can.

Yes, this constraint isn't enforced. The automatically inserted filter 
is not a very clean concept. I'll have to think about it a little more, 
unless there are some ideas.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
  2017-06-26 13:23   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-27 12:08   ` Alberto Garcia
  2017-06-27 12:24     ` Manos Pitsidianakis
  1 sibling, 1 reply; 55+ messages in thread
From: Alberto Garcia @ 2017-06-27 12:08 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf

On Fri 23 Jun 2017 02:46:53 PM CEST, Manos Pitsidianakis wrote:
> This commit gathers ThrottleGroup membership details from
> BlockBackendPublic into ThrottleGroupMember and refactors existing code
> to use the structure.
>
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

Hey Manos, thanks for the patch. It looks good to me in general, I only
have a couple of comments:

>      /* 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.
> +     * then decide the token is the current tgm because chances are
> +     * the current tgm get the current request queued.

I wonder if it's not better to use 'member' instead of 'tgm' in general.
My impression is that the former is clearer and not too long, but I
don't have a very strong opinion so you can keep it like this if you
want.

> -/* Check if an I/O request needs to be throttled, wait and set a timer
> - * if necessary, and schedule the next request using a round robin
> - * algorithm.
> +/* Check if an I/O request needs to be throttled, wait and set a timer if
> + * necessary, and schedule the next request using a round robin algorithm.
>   *

There's a few cases like this when you reformat a paragraph but don't
actually change the text. I think it just adds unnecessary noise to the
diff...

> --- a/include/qemu/throttle.h
> +++ b/include/qemu/throttle.h
> @@ -27,6 +27,7 @@
>  
>  #include "qemu-common.h"
>  #include "qemu/timer.h"
> +#include "qemu/coroutine.h"
>  
>  #define THROTTLE_VALUE_MAX 1000000000000000LL
>  
> @@ -153,4 +154,29 @@ bool throttle_schedule_timer(ThrottleState *ts,
>  
>  void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
>  
> +
> +/* The ThrottleGroupMember structure indicates membership in a ThrottleGroup
> + * and holds related data.
> + */
> +
> +typedef struct ThrottleGroupMember {
> +    /* throttled_reqs_lock protects the CoQueues for throttled requests.  */
> +    CoMutex      throttled_reqs_lock;
> +    CoQueue      throttled_reqs[2];
> +
> +    /* Nonzero if the I/O limits are currently being ignored; generally
> +     * it is zero.  Accessed with atomic operations.
> +     */
> +    unsigned int io_limits_disabled;
> +
> +    /* The following fields are protected by the ThrottleGroup lock.
> +     * See the ThrottleGroup documentation for details.
> +     * throttle_state tells us if I/O limits are configured. */
> +    ThrottleState *throttle_state;
> +    ThrottleTimers throttle_timers;
> +    unsigned       pending_reqs[2];
> +    QLIST_ENTRY(ThrottleGroupMember) round_robin;
> +
> +} ThrottleGroupMember;
> +

Any reason why you add this to throttle.h (which is generic throttling
code independent from the block layer) and not to throttle-groups.h?

Otherwise it all looks good to me, thanks!

Berto

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

* Re: [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember
  2017-06-27 12:08   ` [Qemu-devel] " Alberto Garcia
@ 2017-06-27 12:24     ` Manos Pitsidianakis
  0 siblings, 0 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-27 12:24 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Kevin Wolf

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

On Tue, Jun 27, 2017 at 02:08:54PM +0200, Alberto Garcia wrote:
>On Fri 23 Jun 2017 02:46:53 PM CEST, Manos Pitsidianakis wrote:
>> This commit gathers ThrottleGroup membership details from
>> BlockBackendPublic into ThrottleGroupMember and refactors existing code
>> to use the structure.
>>
>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>
>Hey Manos, thanks for the patch. It looks good to me in general, I only
>have a couple of comments:
>
>>      /* 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.
>> +     * then decide the token is the current tgm because chances are
>> +     * the current tgm get the current request queued.
>
>I wonder if it's not better to use 'member' instead of 'tgm' in general.
>My impression is that the former is clearer and not too long, but I
>don't have a very strong opinion so you can keep it like this if you
>want.

I will change it, no problem,

>> -/* Check if an I/O request needs to be throttled, wait and set a timer
>> - * if necessary, and schedule the next request using a round robin
>> - * algorithm.
>> +/* Check if an I/O request needs to be throttled, wait and set a timer if
>> + * necessary, and schedule the next request using a round robin algorithm.
>>   *
>
>There's a few cases like this when you reformat a paragraph but don't
>actually change the text. I think it just adds unnecessary noise to the
>diff...

Wiki says "It's OK to fix coding style issues in the immediate area (few 
lines) of the lines you're changing" so I left the reformats. Since they 
are noticed they must be too noisey.. I will either remove the changes 
or do a restyling patch later.

>> --- a/include/qemu/throttle.h
>> +++ b/include/qemu/throttle.h
>> @@ -27,6 +27,7 @@
>>
>>  #include "qemu-common.h"
>>  #include "qemu/timer.h"
>> +#include "qemu/coroutine.h"
>>
>>  #define THROTTLE_VALUE_MAX 1000000000000000LL
>>
>> @@ -153,4 +154,29 @@ bool throttle_schedule_timer(ThrottleState *ts,
>>
>>  void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
>>
>> +
>> +/* The ThrottleGroupMember structure indicates membership in a ThrottleGroup
>> + * and holds related data.
>> + */
>> +
>> +typedef struct ThrottleGroupMember {
>> +    /* throttled_reqs_lock protects the CoQueues for throttled requests.  */
>> +    CoMutex      throttled_reqs_lock;
>> +    CoQueue      throttled_reqs[2];
>> +
>> +    /* Nonzero if the I/O limits are currently being ignored; generally
>> +     * it is zero.  Accessed with atomic operations.
>> +     */
>> +    unsigned int io_limits_disabled;
>> +
>> +    /* The following fields are protected by the ThrottleGroup lock.
>> +     * See the ThrottleGroup documentation for details.
>> +     * throttle_state tells us if I/O limits are configured. */
>> +    ThrottleState *throttle_state;
>> +    ThrottleTimers throttle_timers;
>> +    unsigned       pending_reqs[2];
>> +    QLIST_ENTRY(ThrottleGroupMember) round_robin;
>> +
>> +} ThrottleGroupMember;
>> +
>
>Any reason why you add this to throttle.h (which is generic throttling
>code independent from the block layer) and not to throttle-groups.h?

I put it there because it's not directly ThrottleGroup-related, but 
you're right, if throttle.h is block layer free it should remain that 
way.
>
>Otherwise it all looks good to me, thanks!
>
>Berto
>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember Manos Pitsidianakis
  2017-06-26 13:36   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-27 12:39   ` Alberto Garcia
  2017-06-28 11:27   ` Kevin Wolf
  2 siblings, 0 replies; 55+ messages in thread
From: Alberto Garcia @ 2017-06-27 12:39 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf

On Fri 23 Jun 2017 02:46:54 PM CEST, Manos Pitsidianakis wrote:
> timer_cb() needs to know about the current Aio context of the throttle
> request that is woken up. In order to make ThrottleGroupMember backend
> agnostic, this information is stored in an aio_context field instead of
> accessing it from BlockBackend.
>
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

I agree with Stefan's comments, otherwise the patch looks good to me.

Berto

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-26 16:01     ` Manos Pitsidianakis
@ 2017-06-27 12:42       ` Stefan Hajnoczi
  0 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-27 12:42 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Kevin Wolf,
	qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 07:01:18PM +0300, Manos Pitsidianakis wrote:
> On Mon, Jun 26, 2017 at 03:30:55PM +0100, Stefan Hajnoczi wrote:
> > > +static BlockDriver bdrv_throttle = {
> > > +    .format_name                        =   "throttle",
> > > +    .protocol_name                      =   "throttle",
> > > +    .instance_size                      =   sizeof(ThrottleGroupMember),
> > > +
> > > +    .bdrv_file_open                     =   throttle_open,
> > > +    .bdrv_close                         =   throttle_close,
> > > +    .bdrv_co_flush                      =   throttle_co_flush,
> > > +
> > > +    .bdrv_child_perm                    =   bdrv_filter_default_perms,
> > > +
> > > +    .bdrv_getlength                     =   throttle_getlength,
> > > +
> > > +    .bdrv_co_preadv                     =   throttle_co_preadv,
> > > +    .bdrv_co_pwritev                    =   throttle_co_pwritev,
> > > +
> > > +    .bdrv_co_pwrite_zeroes              =   throttle_co_pwrite_zeroes,
> > > +    .bdrv_co_pdiscard                   =   throttle_co_pdiscard,
> > > +
> > > +    .bdrv_recurse_is_first_non_filter   =   bdrv_recurse_is_first_non_filter,
> > > +
> > > +    .bdrv_attach_aio_context            =   throttle_attach_aio_context,
> > > +    .bdrv_detach_aio_context            =   throttle_detach_aio_context,
> > > +
> > > +    .bdrv_reopen_prepare                =   throttle_reopen_prepare,
> > > +    .bdrv_reopen_commit                 =   throttle_reopen_commit,
> > > +    .bdrv_reopen_abort                  =   throttle_reopen_abort,
> > > +
> > > +    .is_filter                          =   true,
> > > +};
> > 
> > Missing:
> > bdrv_co_get_block_status()
> > bdrv_truncate()
> > bdrv_get_info()
> > bdrv_probe_blocksizes()
> > bdrv_probe_geometry()
> > bdrv_media_changed()
> > bdrv_eject()
> > bdrv_lock_medium()
> > bdrv_co_ioctl()
> > 
> > See block/raw-format.c.
> > 
> > I think most of these could be modified in block.c or block/io.c to
> > automatically call bs->file's function if drv doesn't implement them.
> > This way all block drivers would transparently pass them through by
> > default and block/raw-format.c code could be eliminated.
> 
> Are these truly necessary? Because other filter drivers (ie quorum,
> blkverify) don't implement them.

Both quorum and blkverify are rarely used.  This explains why the issue
hasn't been found yet.

These are the callbacks I identified which do not automatically forward
to bs->file.  Therefore the throttle driver will break these features
when bs->file supports them.

That's why I suggest forwarding to bs->file in block.c.  Then individual
drivers do not have to implement these callbacks just to forward to
bs->file.  And if the driver wishes to prohibit a feature, it can
implement the callback and return -ENOTSUP.

You can send this fix as a separate patch series, independent of the
throttle driver.  Once it has been merged the throttle driver will gain
support for these features.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-26 16:26     ` Manos Pitsidianakis
@ 2017-06-27 12:45       ` Stefan Hajnoczi
  2017-06-27 13:34         ` Manos Pitsidianakis
  0 siblings, 1 reply; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-27 12:45 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Kevin Wolf,
	qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 07:26:41PM +0300, Manos Pitsidianakis wrote:
> On Mon, Jun 26, 2017 at 03:30:55PM +0100, Stefan Hajnoczi wrote:
> > > +    bs->file = bdrv_open_child(NULL, options, "file",
> > > +                                    bs, &child_file, false, &local_err);
> > > +
> > > +    if (local_err) {
> > > +        error_propagate(errp, local_err);
> > > +        return -EINVAL;
> > > +    }
> > > +
> > > +    qdict_flatten(options);
> > > +    return throttle_configure_tgm(bs, tgm, options, errp);
> > 
> > Who destroys bs->file on error?
> 
> It is reaped by bdrv_open_inherit() on failure, if I'm not mistaken.
> That's how other drivers handle this as well. Some (eg block/qcow2.c)
> check if bs->file is NULL instead of the error pointer they pass, so
> this is not not very consistent.

Maybe I'm missing it but I don't see relevant bs->file cleanup in
bdrv_open_inherit() or bdrv_open_common().

Please post the exact line where it happens.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-26 15:24     ` Manos Pitsidianakis
@ 2017-06-27 12:57       ` Stefan Hajnoczi
  0 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-27 12:57 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Kevin Wolf,
	qemu-block, Alberto Garcia

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

On Mon, Jun 26, 2017 at 06:24:09PM +0300, Manos Pitsidianakis wrote:
> On Mon, Jun 26, 2017 at 03:52:34PM +0100, Stefan Hajnoczi wrote:
> > > +static void throttle_group_set(Object *obj, Visitor *v, const char * name,
> > > +        void *opaque, Error **errp)
> > > +
> > > +{
> > > +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> > > +    ThrottleConfig cfg = tg->ts.cfg;
> > > +    Error *local_err = NULL;
> > > +    ThrottleParamInfo *info = opaque;
> > > +    int64_t value;
> > 
> > What happens if this property is set while QEMU is already running?
> 
> I assume you mean setting a property while a group has active members and
> requests? My best answer would be "don't do that". I couldn't figure a way
> to do this cleanly. Individual limit changes may render a ThrottleConfig
> invalid, so it should not be allowed. ThrottleGroups and throttle nodes
> should be destroyed and recreated to change limits with this version, but in
> the next it will be done through block_set_io_throttle() which is the
> existing way to change limits and check for validity. This was discussed in
> the proposal about the new syntax we had on the list.

Please ask on IRC or the mailing list if you have questions.

If you are aware of a limitation and don't know the answer then
submitting the code without any comment or question is dangerous.
Reviewers may miss the problem :) and broken code gets merged.

UserCreatableClass has a ->complete() callback.  You can use this to
perform final initialization and then "freeze" the properties by setting
a bool flag.  The property accessor functions can do:

  if (tg->init_complete) {
      error_setg(errp, "Property modification is not allowed at run-time");
      return;
  }

Something like this is used by
backends/hostmem.c:host_memory_backend_set_size(), for example.

> > 
> > > +        goto out;
> > > +    }
> > 
> > This doesn't validate inputs properly for non int64_t types.
> > 
> > I'm also worried that the command-line bps=,iops=,... options do not
> > have unsigned or double types.  Input ranges and validation should match
> > the QEMU command-line (I know this is a bit of a pain with QOM since the
> > property types are different from QEMU option types).
> 
> I don't know what should be done about this, to be honest, except for
> manually checking the limits for each datatype in the QOM setters.

I believe all throttling parameter types are int64_t in QemuOpts.  If we
want to be compatible with the command-line parameters then they should
also be int64_t here instead of unsigned int or double.

This approach makes sense from the QMP user perspective.  QMP clients
shouldn't have to deal with different data types depending on which
throttling API they use.  Let's keep it consistent - there's no real
drawback to using int64_t.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-26 16:58     ` Manos Pitsidianakis
@ 2017-06-27 13:02       ` Stefan Hajnoczi
  2017-06-27 16:05       ` Alberto Garcia
  1 sibling, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-27 13:02 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Kevin Wolf,
	qemu-block, Alberto Garcia
  Cc: Pradeep Jagadeesh

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

On Mon, Jun 26, 2017 at 07:58:32PM +0300, Manos Pitsidianakis wrote:
> On Mon, Jun 26, 2017 at 03:52:34PM +0100, Stefan Hajnoczi wrote:
> > On Fri, Jun 23, 2017 at 03:46:56PM +0300, Manos Pitsidianakis wrote:
> > > +static bool throttle_group_exists(const char *name)
> > > +{
> > > +    ThrottleGroup *iter;
> > > +    bool ret = false;
> > > +
> > > +    qemu_mutex_lock(&throttle_groups_lock);
> > 
> > Not sure if this lock or the throttle_groups list are necessary.
> > 
> > Have you seen iothread.c:qmp_query_iothreads()?  All objects are put
> > into a container (the parent object), so you can just iterate over its
> > children.  There's no need for a separate list because QOM already has
> > all the objects.
> > 
> 
> > > +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
> > > +{
> > > +    char *name = NULL;
> > > +    Error *local_error = NULL;
> > > +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> > > +
> > > +    name = object_get_canonical_path_component(OBJECT(obj));
> > > +    if (throttle_group_exists(name)) {
> > > +        error_setg(&local_error, "A throttle group with this name already \
> > > +                                  exists.");
> > > +        goto ret;
> > > +    }
> > 
> > QOM should enforce unique id=<ID>.  I don't think this is necessary.
> > 
> > > +
> > > +    qemu_mutex_lock(&throttle_groups_lock);
> > > +    tg->name = name;
> > > +    qemu_mutex_init(&tg->lock);
> > > +    QLIST_INIT(&tg->head);
> > > +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> > > +    tg->refcount++;
> > > +    qemu_mutex_unlock(&throttle_groups_lock);
> > > +
> 
> Sorry for the multiple replies but I just remembered this.
> 
> This is necessary because throttle groups are created by other interfaces as
> well. Of course block/throttle-groups.c could use only QOM objects
> internally to eliminate the housekeeping.

I suggest all throttle group objects are visible in QOM.  Who are the
non-QOM users?

I have CCed Pradeep who has been working on virtio-9p throttling.

Pradeep: You may be interested in this series, which makes the throttle
group a QOM object (-object throttle-group,id=group0,bps=1235678).  In
other words, groups are becoming first-class objects instead of being
hidden behind the member devices that use them.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 7/8] block: remove legacy I/O throttling
  2017-06-26 22:45     ` Manos Pitsidianakis
@ 2017-06-27 13:08       ` Stefan Hajnoczi
  0 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-27 13:08 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Kevin Wolf,
	qemu-block, Alberto Garcia

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

On Tue, Jun 27, 2017 at 01:45:07AM +0300, Manos Pitsidianakis wrote:
> On Mon, Jun 26, 2017 at 04:44:44PM +0100, Stefan Hajnoczi wrote:
> > On Fri, Jun 23, 2017 at 03:46:59PM +0300, Manos Pitsidianakis wrote:
> > > -void blk_io_limits_disable(BlockBackend *blk)
> > > +void blk_io_limits_disable(BlockBackend *blk, Error **errp)
> > >  {
> > > -    assert(blk->public.throttle_group_member.throttle_state);
> > > -    bdrv_drained_begin(blk_bs(blk));
> > > -    throttle_group_unregister_tgm(&blk->public.throttle_group_member);
> > > -    bdrv_drained_end(blk_bs(blk));
> > > +    Error *local_err = NULL;
> > > +    BlockDriverState *bs, *throttle_node;
> > > +
> > > +    throttle_node = blk_get_public(blk)->throttle_node;
> > > +
> > > +    assert(throttle_node && throttle_node->refcnt == 1);
> > 
> > I'm not sure if we can enforce refcnt == 1.  What stops other graph
> > manipulation operations from inserting a node above or a BB that uses
> > throttle_node as the root?
> 
> Is this possible unless the user explicitly does this? I suppose going down
> the path and removing it from any place is okay. If the throttle_node has
> more than one parent then the result would be invalid anyway. I don't see
> anything in the code doing this (removing a BDS from a BB->leaf path) or
> wrapping it in some way, is that what should be done?

We cannot assume the user will keep their hands off the graph :).

In general, QEMU cannot assume that external interfaces (e.g.
command-line, monitor, VNC, ...) are only called in convenient and safe
ways.

The code must handle all possible situations.  Usually the simplest
solution is to reject requests that would put QEMU into an invalid
state.

> > 
> > > +
> > > +    /* ref throttle_node's child bs so that it isn't lost when throttle_node is
> > > +     * destroyed */
> > > +    bdrv_ref(bs);
> > > +
> > > +    /* this destroys throttle_node */
> > > +    blk_remove_bs(blk);
> > 
> > This assumes that throttle_node is the top node.  How is this constraint
> > enforced?
> 
> 
> Kevin had said in a previous discussion:
> > The throttle node affects only the backing file now, but not the new
> > top-level node. With manually created filter nodes we may need to
> > provide a way so that the QMP command tells where in the tree the
> > snapshot is actually taken. With automatically created ones, we need to
> > make sure that they always stay on top.
> > 
> > Similar problems arise when block jobs manipulate the graph. For
> > example, think of a commit block job, which will remove the topmost node
> > from the backing file chain. We don't want it to remove the throttling
> > filter together with the qcow2 node. (Or do we? It might be something
> > that needs to be configurable.)
> > 
> > We also have several QMP that currently default to working on the
> > topmost image of the chain. Some of them may actually want to work on
> > the topmost node that isn't an automatically inserted filter.
> > 
> > 
> > I hope this helps you understand why I keep saying that automatic
> > insertion/removal of filter nodes is tricky and we should do it as
> > little as we possibly can.
> 
> Yes, this constraint isn't enforced. The automatically inserted filter is
> not a very clean concept. I'll have to think about it a little more, unless
> there are some ideas.

The block layer has an "op blockers" and permissions API to reject
operations that would violate the graph constraints.  Kevin has recently
worked on it and I hope he can give you some advice in this area.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
  2017-06-26 14:55   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-27 13:12   ` Eric Blake
  2017-06-28 13:35   ` Alberto Garcia
  2017-06-28 15:50   ` Kevin Wolf
  3 siblings, 0 replies; 55+ messages in thread
From: Eric Blake @ 2017-06-27 13:12 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel
  Cc: Kevin Wolf, Alberto Garcia, Stefan Hajnoczi, qemu-block

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

On 06/23/2017 07:46 AM, Manos Pitsidianakis wrote:
> This is needed to configure throttle filter driver nodes with QAPI.
> 
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
>  qapi/block-core.json | 19 ++++++++++++++++++-
>  1 file changed, 18 insertions(+), 1 deletion(-)
> 
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index f85c2235c7..1d4afafe8c 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -2119,7 +2119,7 @@
>              'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
>              'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
>              'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
> -            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
> +            'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }

We already have a '@vxhs: Since 2.10' comment, you should add a similar
one for @throttle.


> +
> +##
> +# @BlockdevOptionsThrottle:
> +#
> +# Driver specific block device options for Throttle
> +#
> +# @throttling-group: the name of the throttling group to use
> +#
> +# @options:        BlockIOThrottle options
> +# Since: 2.9

You missed 2.9; this should be 2.10.

> +##
> +{ 'struct': 'BlockdevOptionsThrottle',
> +  'data': { 'throttling-group': 'str',
> +            'file' : 'BlockdevRef',

Missing documentation for 'file'.

> +            '*options' : 'BlockIOThrottle'
> +             } }
> 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


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

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-27 12:45       ` Stefan Hajnoczi
@ 2017-06-27 13:34         ` Manos Pitsidianakis
  2017-06-28 12:11           ` Stefan Hajnoczi
  0 siblings, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-27 13:34 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Stefan Hajnoczi, qemu-devel, Kevin Wolf, qemu-block, Alberto Garcia

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

On Tue, Jun 27, 2017 at 01:45:40PM +0100, Stefan Hajnoczi wrote:
>On Mon, Jun 26, 2017 at 07:26:41PM +0300, Manos Pitsidianakis wrote:
>> On Mon, Jun 26, 2017 at 03:30:55PM +0100, Stefan Hajnoczi wrote:
>> > > +    bs->file = bdrv_open_child(NULL, options, "file",
>> > > +                                    bs, &child_file, false, &local_err);
>> > > +
>> > > +    if (local_err) {
>> > > +        error_propagate(errp, local_err);
>> > > +        return -EINVAL;
>> > > +    }
>> > > +
>> > > +    qdict_flatten(options);
>> > > +    return throttle_configure_tgm(bs, tgm, options, errp);
>> >
>> > Who destroys bs->file on error?
>>
>> It is reaped by bdrv_open_inherit() on failure, if I'm not mistaken.
>> That's how other drivers handle this as well. Some (eg block/qcow2.c)
>> check if bs->file is NULL instead of the error pointer they pass, so
>> this is not not very consistent.
>
>Maybe I'm missing it but I don't see relevant bs->file cleanup in
>bdrv_open_inherit() or bdrv_open_common().
>
>Please post the exact line where it happens.
>
>Stefan

Relevant commit: de234897b60e034ba94b307fc289e2dc692c9251 block: Do not 
unref bs->file on error in BD's open

bdrv_open_inherit() does this on failure:

fail:
    blk_unref(file);
    if (bs->file != NULL) {
        bdrv_unref_child(bs, bs->file);
    }

While looking into this I noticed bdrv_new_open_driver() doesn't handle 
bs->file on failure. It simply unrefs the bs but because its child's ref 
still remains, it is leaked.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-26 16:58     ` Manos Pitsidianakis
  2017-06-27 13:02       ` Stefan Hajnoczi
@ 2017-06-27 16:05       ` Alberto Garcia
  2017-06-27 16:12         ` Manos Pitsidianakis
  2017-06-28 12:07         ` Stefan Hajnoczi
  1 sibling, 2 replies; 55+ messages in thread
From: Alberto Garcia @ 2017-06-27 16:05 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi
  Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

On Mon 26 Jun 2017 06:58:32 PM CEST, Manos Pitsidianakis wrote:
> On Mon, Jun 26, 2017 at 03:52:34PM +0100, Stefan Hajnoczi wrote:
>>On Fri, Jun 23, 2017 at 03:46:56PM +0300, Manos Pitsidianakis wrote:
>>> +static bool throttle_group_exists(const char *name)
>>> +{
>>> +    ThrottleGroup *iter;
>>> +    bool ret = false;
>>> +
>>> +    qemu_mutex_lock(&throttle_groups_lock);
>>
>>Not sure if this lock or the throttle_groups list are necessary.

As Manos says accesses to the throttle_groups list need to be locked.

What I don't like at first sight is the code duplication in
throttle_group_incref() and throttle_group_obj_complete().

>>Have you seen iothread.c:qmp_query_iothreads()?  All objects are put
>>into a container (the parent object), so you can just iterate over its
>>children.  There's no need for a separate list because QOM already has
>>all the objects.

I haven't looked into this yet but it could be a solution.

Berto

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-27 16:05       ` Alberto Garcia
@ 2017-06-27 16:12         ` Manos Pitsidianakis
  2017-06-28 12:07         ` Stefan Hajnoczi
  1 sibling, 0 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-27 16:12 UTC (permalink / raw)
  To: Alberto Garcia
  Cc: Stefan Hajnoczi, qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Tue, Jun 27, 2017 at 06:05:55PM +0200, Alberto Garcia wrote:
>On Mon 26 Jun 2017 06:58:32 PM CEST, Manos Pitsidianakis wrote:
>> On Mon, Jun 26, 2017 at 03:52:34PM +0100, Stefan Hajnoczi wrote:
>>>On Fri, Jun 23, 2017 at 03:46:56PM +0300, Manos Pitsidianakis wrote:
>>>> +static bool throttle_group_exists(const char *name)
>>>> +{
>>>> +    ThrottleGroup *iter;
>>>> +    bool ret = false;
>>>> +
>>>> +    qemu_mutex_lock(&throttle_groups_lock);
>>>
>>>Not sure if this lock or the throttle_groups list are necessary.
>
>As Manos says accesses to the throttle_groups list need to be locked.
>
>What I don't like at first sight is the code duplication in
>throttle_group_incref() and throttle_group_obj_complete().
>
>>>Have you seen iothread.c:qmp_query_iothreads()?  All objects are put
>>>into a container (the parent object), so you can just iterate over its
>>>children.  There's no need for a separate list because QOM already has
>>>all the objects.
>
>I haven't looked into this yet but it could be a solution.

If throttle_groups_register_blk() uses QOM instead of calling 
throttle_group_incref() the duplication could be eliminated. Then all of 
throttle-groups.c uses QOM internally. I don't see any reason why not do 
this.

>Berto
>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 8/8] block: add throttle block filter driver interface tests
  2017-06-23 12:47 ` [Qemu-devel] [PATCH RFC v3 8/8] block: add throttle block filter driver interface tests Manos Pitsidianakis
@ 2017-06-28 11:18   ` Kevin Wolf
  0 siblings, 0 replies; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 11:18 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Alberto Garcia

Am 23.06.2017 um 14:47 hat Manos Pitsidianakis geschrieben:
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

This one doesn't apply because the reference output contains a too long
line and a newline was inserted. I think QMP pretty printing can help to
avoid overly long lines.

Kevin

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

* Re: [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember Manos Pitsidianakis
  2017-06-26 13:36   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-06-27 12:39   ` [Qemu-devel] " Alberto Garcia
@ 2017-06-28 11:27   ` Kevin Wolf
  2017-06-28 12:15     ` Manos Pitsidianakis
  2 siblings, 1 reply; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 11:27 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Alberto Garcia

Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
> timer_cb() needs to know about the current Aio context of the throttle
> request that is woken up. In order to make ThrottleGroupMember backend
> agnostic, this information is stored in an aio_context field instead of
> accessing it from BlockBackend.
> 
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

You're copying the AioContext when the BlockBackend is registered for
the throttle group, but what keeps both sides in sync when the context
is changed later on? Don't we need to update the ThrottleGroupMember in
blk_set_aio_context?

Kevin

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM
  2017-06-27 16:05       ` Alberto Garcia
  2017-06-27 16:12         ` Manos Pitsidianakis
@ 2017-06-28 12:07         ` Stefan Hajnoczi
  1 sibling, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-28 12:07 UTC (permalink / raw)
  To: Alberto Garcia
  Cc: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Kevin Wolf, qemu-block

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

On Tue, Jun 27, 2017 at 06:05:55PM +0200, Alberto Garcia wrote:
> On Mon 26 Jun 2017 06:58:32 PM CEST, Manos Pitsidianakis wrote:
> > On Mon, Jun 26, 2017 at 03:52:34PM +0100, Stefan Hajnoczi wrote:
> >>On Fri, Jun 23, 2017 at 03:46:56PM +0300, Manos Pitsidianakis wrote:
> >>> +static bool throttle_group_exists(const char *name)
> >>> +{
> >>> +    ThrottleGroup *iter;
> >>> +    bool ret = false;
> >>> +
> >>> +    qemu_mutex_lock(&throttle_groups_lock);
> >>
> >>Not sure if this lock or the throttle_groups list are necessary.
> 
> As Manos says accesses to the throttle_groups list need to be locked.

Explicit locking is only necessary if the list is accessed outside the
QEMU global mutex.  If the monitor is the only thing that accesses the
list then a lock is not necessary.

Anyway, this point might be moot if every ThrottleGroup is a QOM object
and we drop this code in favor of using QOM APIs to find and iterate
over objects.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-27 13:34         ` Manos Pitsidianakis
@ 2017-06-28 12:11           ` Stefan Hajnoczi
  0 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2017-06-28 12:11 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Kevin Wolf,
	qemu-block, Alberto Garcia

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

On Tue, Jun 27, 2017 at 04:34:22PM +0300, Manos Pitsidianakis wrote:
> On Tue, Jun 27, 2017 at 01:45:40PM +0100, Stefan Hajnoczi wrote:
> > On Mon, Jun 26, 2017 at 07:26:41PM +0300, Manos Pitsidianakis wrote:
> > > On Mon, Jun 26, 2017 at 03:30:55PM +0100, Stefan Hajnoczi wrote:
> > > > > +    bs->file = bdrv_open_child(NULL, options, "file",
> > > > > +                                    bs, &child_file, false, &local_err);
> > > > > +
> > > > > +    if (local_err) {
> > > > > +        error_propagate(errp, local_err);
> > > > > +        return -EINVAL;
> > > > > +    }
> > > > > +
> > > > > +    qdict_flatten(options);
> > > > > +    return throttle_configure_tgm(bs, tgm, options, errp);
> > > >
> > > > Who destroys bs->file on error?
> > > 
> > > It is reaped by bdrv_open_inherit() on failure, if I'm not mistaken.
> > > That's how other drivers handle this as well. Some (eg block/qcow2.c)
> > > check if bs->file is NULL instead of the error pointer they pass, so
> > > this is not not very consistent.
> > 
> > Maybe I'm missing it but I don't see relevant bs->file cleanup in
> > bdrv_open_inherit() or bdrv_open_common().
> > 
> > Please post the exact line where it happens.
> > 
> > Stefan
> 
> Relevant commit: de234897b60e034ba94b307fc289e2dc692c9251 block: Do not
> unref bs->file on error in BD's open
> 
> bdrv_open_inherit() does this on failure:
> 
> fail:
>    blk_unref(file);
>    if (bs->file != NULL) {
>        bdrv_unref_child(bs, bs->file);
>    }

Thanks, you are right.  I missed it.

> While looking into this I noticed bdrv_new_open_driver() doesn't handle
> bs->file on failure. It simply unrefs the bs but because its child's ref
> still remains, it is leaked.

That's a good candidate for a separate bug fix patch.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember
  2017-06-28 11:27   ` Kevin Wolf
@ 2017-06-28 12:15     ` Manos Pitsidianakis
  2017-06-28 12:44       ` Kevin Wolf
  0 siblings, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-28 12:15 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Alberto Garcia

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

On Wed, Jun 28, 2017 at 01:27:36PM +0200, Kevin Wolf wrote:
>Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
>> timer_cb() needs to know about the current Aio context of the throttle
>> request that is woken up. In order to make ThrottleGroupMember backend
>> agnostic, this information is stored in an aio_context field instead of
>> accessing it from BlockBackend.
>>
>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>
>You're copying the AioContext when the BlockBackend is registered for
>the throttle group, but what keeps both sides in sync when the context
>is changed later on? Don't we need to update the ThrottleGroupMember in
>blk_set_aio_context?

blk_set_aio_context calls throttle_timers_attach_aio_context which 
updates this. Though as Alberto said util/throttle.c should not know 
about ThrottleGroupMember. This is not needed in the later patches 
because the ThrottleGroupMember's aio_context gets updated as a node in 
the driver's bdrv_attach_aio_context

We can add a new function in block/throttle.c that updates a member's 
aio context but I'm not sure if it's really needed if members are only 
used in throttle nodes.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember
  2017-06-28 12:15     ` Manos Pitsidianakis
@ 2017-06-28 12:44       ` Kevin Wolf
  0 siblings, 0 replies; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 12:44 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel, qemu-block, Stefan Hajnoczi,
	Alberto Garcia

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

Am 28.06.2017 um 14:15 hat Manos Pitsidianakis geschrieben:
> On Wed, Jun 28, 2017 at 01:27:36PM +0200, Kevin Wolf wrote:
> >Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
> >>timer_cb() needs to know about the current Aio context of the throttle
> >>request that is woken up. In order to make ThrottleGroupMember backend
> >>agnostic, this information is stored in an aio_context field instead of
> >>accessing it from BlockBackend.
> >>
> >>Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> >
> >You're copying the AioContext when the BlockBackend is registered for
> >the throttle group, but what keeps both sides in sync when the context
> >is changed later on? Don't we need to update the ThrottleGroupMember in
> >blk_set_aio_context?
> 
> blk_set_aio_context calls throttle_timers_attach_aio_context which
> updates this. Though as Alberto said util/throttle.c should not know
> about ThrottleGroupMember. This is not needed in the later patches
> because the ThrottleGroupMember's aio_context gets updated as a node
> in the driver's bdrv_attach_aio_context
> 
> We can add a new function in block/throttle.c that updates a
> member's aio context but I'm not sure if it's really needed if
> members are only used in throttle nodes.

Oh, I looked at the final state after the series instead of this very
commit, so I missed the existing calls in blk_set_aio_context().

My bad, sorry for the noise.

Kevin

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
  2017-06-26 14:55   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-06-27 13:12   ` [Qemu-devel] " Eric Blake
@ 2017-06-28 13:35   ` Alberto Garcia
  2017-06-28 13:42     ` Manos Pitsidianakis
  2017-06-28 15:50   ` Kevin Wolf
  3 siblings, 1 reply; 55+ messages in thread
From: Alberto Garcia @ 2017-06-28 13:35 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf

On Fri 23 Jun 2017 02:46:57 PM CEST, Manos Pitsidianakis wrote:

> +# @BlockdevOptionsThrottle:
> +#
> +# Driver specific block device options for Throttle
> +#

I would put this earlier in the json file, together with the rest of the
BlockdevOptions* structs.

> +# @throttling-group: the name of the throttling group to use

Why not call it simply "group" ?

> +#
> +# @options:        BlockIOThrottle options
> +# Since: 2.9
> +##
> +{ 'struct': 'BlockdevOptionsThrottle',
> +  'data': { 'throttling-group': 'str',
> +            'file' : 'BlockdevRef',
> +            '*options' : 'BlockIOThrottle'
> +             } }

Not sure if 'file' is the best name for the field ("child"?), but I'm
fine with it.

Berto

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

* Re: [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-28 13:35   ` Alberto Garcia
@ 2017-06-28 13:42     ` Manos Pitsidianakis
  0 siblings, 0 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-28 13:42 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Kevin Wolf

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

On Wed, Jun 28, 2017 at 03:35:48PM +0200, Alberto Garcia wrote:
>On Fri 23 Jun 2017 02:46:57 PM CEST, Manos Pitsidianakis wrote:
>
>> +# @BlockdevOptionsThrottle:
>> +#
>> +# Driver specific block device options for Throttle
>> +#
>
>I would put this earlier in the json file, together with the rest of the
>BlockdevOptions* structs.
>
>> +# @throttling-group: the name of the throttling group to use
>
>Why not call it simply "group" ?

Sure! :)
>
>> +#
>> +# @options:        BlockIOThrottle options
>> +# Since: 2.9
>> +##
>> +{ 'struct': 'BlockdevOptionsThrottle',
>> +  'data': { 'throttling-group': 'str',
>> +            'file' : 'BlockdevRef',
>> +            '*options' : 'BlockIOThrottle'
>> +             } }
>
>Not sure if 'file' is the best name for the field ("child"?), but I'm
>fine with it.

There doesn't seem to be a consistent naming scheme in block-core.json 
("file", "image" are candidates) so I put it file after bs->file. 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver()
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver() Manos Pitsidianakis
  2017-06-26 15:11   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-28 13:42   ` Alberto Garcia
  2017-06-28 13:47     ` Manos Pitsidianakis
  1 sibling, 1 reply; 55+ messages in thread
From: Alberto Garcia @ 2017-06-28 13:42 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: qemu-block, Stefan Hajnoczi, Kevin Wolf

On Fri 23 Jun 2017 02:46:58 PM CEST, Manos Pitsidianakis wrote:
>  BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
> -                                       int flags, Error **errp)
> +                                       int flags, QDict *options, Error **errp)
>  {
>      BlockDriverState *bs;
>      int ret;
>  
>      bs = bdrv_new();
>      bs->open_flags = flags;
> -    bs->explicit_options = qdict_new();
> -    bs->options = qdict_new();
> +    if (options) {
> +        bs->explicit_options = qdict_clone_shallow(options);
> +        bs->options = qdict_clone_shallow(options);
> +    } else {
> +        bs->explicit_options = qdict_new();
> +        bs->options = qdict_new();
> +    }
>      bs->opaque = NULL;
>  
>      update_options_from_flags(bs->options, flags);
>  
> -    ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
> +    ret = bdrv_open_driver(bs, drv, node_name, options, flags, errp);

Why this last change? In the default case you're now passing NULL
instead of the QDict created with qdict_new().

Berto

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

* Re: [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver()
  2017-06-28 13:42   ` [Qemu-devel] " Alberto Garcia
@ 2017-06-28 13:47     ` Manos Pitsidianakis
  0 siblings, 0 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-28 13:47 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Kevin Wolf

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

On Wed, Jun 28, 2017 at 03:42:41PM +0200, Alberto Garcia wrote:
>On Fri 23 Jun 2017 02:46:58 PM CEST, Manos Pitsidianakis wrote:
>>  BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
>> -                                       int flags, Error **errp)
>> +                                       int flags, QDict *options, Error **errp)
>>  {
>>      BlockDriverState *bs;
>>      int ret;
>>
>>      bs = bdrv_new();
>>      bs->open_flags = flags;
>> -    bs->explicit_options = qdict_new();
>> -    bs->options = qdict_new();
>> +    if (options) {
>> +        bs->explicit_options = qdict_clone_shallow(options);
>> +        bs->options = qdict_clone_shallow(options);
>> +    } else {
>> +        bs->explicit_options = qdict_new();
>> +        bs->options = qdict_new();
>> +    }
>>      bs->opaque = NULL;
>>
>>      update_options_from_flags(bs->options, flags);
>>
>> -    ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
>> +    ret = bdrv_open_driver(bs, drv, node_name, options, flags, errp);
>
>Why this last change? In the default case you're now passing NULL
>instead of the QDict created with qdict_new().

Duh, nice catch!

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver Manos Pitsidianakis
                     ` (2 preceding siblings ...)
  2017-06-26 14:34   ` Stefan Hajnoczi
@ 2017-06-28 14:40   ` Kevin Wolf
  2017-06-28 15:22     ` Manos Pitsidianakis
  3 siblings, 1 reply; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 14:40 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Alberto Garcia

Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
> block/throttle.c uses existing I/O throttle infrastructure inside a
> block filter driver. I/O operations are intercepted in the filter's
> read/write coroutines, and referred to block/throttle-groups.c
> 
> The driver can be used with the command
> -drive driver=throttle,file.filename=foo.qcow2,iops-total=...
> The configuration flags and semantics are identical to the hardcoded
> throttling ones.
> 
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
>  block/Makefile.objs             |   1 +
>  block/throttle.c                | 427 ++++++++++++++++++++++++++++++++++++++++
>  include/qemu/throttle-options.h |  60 ++++--
>  3 files changed, 469 insertions(+), 19 deletions(-)
>  create mode 100644 block/throttle.c
> 
> diff --git a/block/Makefile.objs b/block/Makefile.objs
> index ea955302c8..bb811a4d01 100644
> --- a/block/Makefile.objs
> +++ b/block/Makefile.objs
> @@ -25,6 +25,7 @@ block-obj-y += accounting.o dirty-bitmap.o
>  block-obj-y += write-threshold.o
>  block-obj-y += backup.o
>  block-obj-$(CONFIG_REPLICATION) += replication.o
> +block-obj-y += throttle.o
>  
>  block-obj-y += crypto.o
>  
> diff --git a/block/throttle.c b/block/throttle.c
> new file mode 100644
> index 0000000000..0c17051161
> --- /dev/null
> +++ b/block/throttle.c
> @@ -0,0 +1,427 @@
> +/*
> + * QEMU block throttling filter driver infrastructure
> + *
> + * Copyright (c) 2017 Manos Pitsidianakis
> + *
> + * 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/>.
> + */

Please consider using the LGPL. We're still hoping to turn the block
layer into a library one day, and almost all code in it is licensed
liberally (MIT or LGPL).

> +#include "qemu/osdep.h"
> +#include "block/throttle-groups.h"
> +#include "qemu/throttle-options.h"
> +#include "qapi/error.h"
> +
> +
> +static QemuOptsList throttle_opts = {
> +    .name = "throttle",
> +    .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
> +    .desc = {
> +        {
> +            .name = QEMU_OPT_IOPS_TOTAL,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "limit total I/O operations per second",
> +        },{
> +            .name = QEMU_OPT_IOPS_READ,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "limit read operations per second",
> +        },{
> +            .name = QEMU_OPT_IOPS_WRITE,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "limit write operations per second",
> +        },{
> +            .name = QEMU_OPT_BPS_TOTAL,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "limit total bytes per second",
> +        },{
> +            .name = QEMU_OPT_BPS_READ,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "limit read bytes per second",
> +        },{
> +            .name = QEMU_OPT_BPS_WRITE,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "limit write bytes per second",
> +        },{
> +            .name = QEMU_OPT_IOPS_TOTAL_MAX,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "I/O operations burst",
> +        },{
> +            .name = QEMU_OPT_IOPS_READ_MAX,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "I/O operations read burst",
> +        },{
> +            .name = QEMU_OPT_IOPS_WRITE_MAX,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "I/O operations write burst",
> +        },{
> +            .name = QEMU_OPT_BPS_TOTAL_MAX,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "total bytes burst",
> +        },{
> +            .name = QEMU_OPT_BPS_READ_MAX,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "total bytes read burst",
> +        },{
> +            .name = QEMU_OPT_BPS_WRITE_MAX,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "total bytes write burst",
> +        },{
> +            .name = QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "length of the iopstotalmax burst period, in seconds",
> +        },{
> +            .name = QEMU_OPT_IOPS_READ_MAX_LENGTH,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "length of the iopsreadmax burst period, in seconds",
> +        },{
> +            .name = QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "length of the iopswritemax burst period, in seconds",
> +        },{
> +            .name = QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "length of the bpstotalmax burst period, in seconds",
> +        },{
> +            .name = QEMU_OPT_BPS_READ_MAX_LENGTH,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "length of the bpsreadmax burst period, in seconds",
> +        },{
> +            .name = QEMU_OPT_BPS_WRITE_MAX_LENGTH,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "length of the bpswritemax burst period, in seconds",
> +        },{
> +            .name = QEMU_OPT_IOPS_SIZE,
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "when limiting by iops max size of an I/O in bytes",
> +        },
> +        {
> +            .name = QEMU_OPT_THROTTLE_GROUP_NAME,
> +            .type = QEMU_OPT_STRING,
> +            .help = "throttle group name",
> +        },
> +        { /* end of list */ }
> +    },
> +};
> +
> +/* Extract ThrottleConfig options. Assumes cfg is initialized and will be
> + * checked for validity.
> + */
> +
> +static void throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg)
> +{
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL)) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ)) {
> +        cfg->buckets[THROTTLE_BPS_READ].avg  =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE)) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].avg =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL)) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ)) {
> +        cfg->buckets[THROTTLE_OPS_READ].avg =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE)) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].avg =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX)) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].max =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX)) {
> +        cfg->buckets[THROTTLE_BPS_READ].max  =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX)) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].max =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX)) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].max =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX)) {
> +        cfg->buckets[THROTTLE_OPS_READ].max =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX)) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].max =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX_LENGTH)) {
> +        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
> +        cfg->buckets[THROTTLE_OPS_READ].burst_length =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_SIZE)) {
> +        cfg->op_size =
> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_SIZE, 0);
> +    }
> +}
> +
> +static int throttle_configure_tgm(BlockDriverState *bs, ThrottleGroupMember *tgm,
> +                                                    QDict *options, Error **errp)

Both lines exceed 80 characters. The indentation is off, too: QDict on
the second line should be aligned with BlockDriverState on the first
one.

> +{
> +    int ret = 0;
> +    ThrottleState *ts;
> +    ThrottleTimers *tt;
> +    ThrottleConfig cfg;
> +    QemuOpts *opts = NULL;
> +    const char *group_name = NULL;
> +    Error *local_err = NULL;
> +
> +    opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return -EINVAL;
> +    }
> +    qemu_opts_absorb_qdict(opts, options, &local_err);
> +    if (local_err) {
> +        goto err;
> +    }
> +
> +    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
> +    if (!group_name) {
> +        group_name = bdrv_get_device_or_node_name(bs);

This is a legacy function, we should avoid adding new callers.

Worse yet, if the group isn't specified, we want to create a new
group internally just for this throttle node. But nothing stops the user
from creating a group that is named like the device or node name of
another throttle node, so we might end up attaching to an existing
throttle group instead!

I think throttle groups should be anonymous rather than having a default
name if they are created implicitly.

> +        if (!strlen(group_name)) {

More efficiently written as if (!*group_name), but it should go away
anyway when you address the above comment.

> +            error_setg(&local_err,
> +                       "A group name must be specified for this device.");

You can directly set errp and avoid the error_propagate() later.

> +            goto err;
> +        }
> +    }
> +
> +    tgm->aio_context = bdrv_get_aio_context(bs);
> +    /* Register membership to group with name group_name */
> +    throttle_group_register_tgm(tgm, group_name);
> +
> +    ts = tgm->throttle_state;
> +    /* Copy previous configuration */
> +    throttle_get_config(ts, &cfg);
> +
> +    /* Change limits if user has specified them */
> +    throttle_extract_options(opts, &cfg);
> +    if (!throttle_is_valid(&cfg, &local_err)) {

Here errp can directly be used, too.

You only need the &local_err idiom if you want to check whether an error
was set (because the caller might pass errp == NULL and then you can't
tell whether an error occurred or not).

> +        throttle_group_unregister_tgm(tgm);
> +        goto err;
> +    }
> +    tt = &tgm->throttle_timers;
> +    /* Update group configuration */
> +    throttle_config(ts, tt, &cfg);
> +
> +    qemu_co_queue_init(&tgm->throttled_reqs[0]);
> +    qemu_co_queue_init(&tgm->throttled_reqs[1]);
> +
> +    goto fin;

I prefer an explicit ret = 0 right here because ret tends to be used to
store the return value for all kinds of functions. In this specific
case, it's still 0 from the initialisation, but that's easy to break
accidentally in a future patch.

> +
> +err:
> +    error_propagate(errp, local_err);
> +    ret = -EINVAL;
> +fin:
> +    qemu_opts_del(opts);
> +    return ret;
> +}
> +
> +static int throttle_open(BlockDriverState *bs, QDict *options,
> +                            int flags, Error **errp)
> +{
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    Error *local_err = NULL;
> +
> +    bs->open_flags = flags;
> +    bs->file = bdrv_open_child(NULL, options, "file",
> +                                    bs, &child_file, false, &local_err);
> +
> +    if (local_err) {

If you check bs->file == NULL instead, you can avoid local_err.

> +        error_propagate(errp, local_err);
> +        return -EINVAL;
> +    }
> +
> +    qdict_flatten(options);

Why do you need this? options should already be flattened.

> +    return throttle_configure_tgm(bs, tgm, options, errp);
> +}
> +
> +static void throttle_close(BlockDriverState *bs)
> +{
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    bdrv_drained_begin(bs);
> +    throttle_group_unregister_tgm(tgm);
> +    bdrv_drained_end(bs);
> +    return;

This is a useless return statement. I think I've seen compilers warn
about it (and we use -Werror, so this would be a build failure on them).

bdrv_drained_begin/end should be unnecessary in .bdrv_close
implementations, we always drain all requests before calling the
callback.

> +}
> +
> +
> +static int64_t throttle_getlength(BlockDriverState *bs)
> +{
> +    return bdrv_getlength(bs->file->bs);
> +}
> +
> +
> +static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, uint64_t offset,

This line exceeds 80 characters.

> +                                            uint64_t bytes, QEMUIOVector *qiov,
> +                                            int flags)

Indentation is off by one.

> +{
> +
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    throttle_group_co_io_limits_intercept(tgm, bytes, false);
> +
> +    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
> +}
> +
> +static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs, uint64_t offset,

Another long line.

> +                                            uint64_t bytes, QEMUIOVector *qiov,
> +                                            int flags)
> +{
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    throttle_group_co_io_limits_intercept(tgm, bytes, true);
> +
> +    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
> +}

Kevin

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

* Re: [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-28 14:40   ` [Qemu-devel] " Kevin Wolf
@ 2017-06-28 15:22     ` Manos Pitsidianakis
  2017-06-28 15:36       ` Kevin Wolf
  0 siblings, 1 reply; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-28 15:22 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Alberto Garcia

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

On Wed, Jun 28, 2017 at 04:40:12PM +0200, Kevin Wolf wrote:
>Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
>> block/throttle.c uses existing I/O throttle infrastructure inside a
>> block filter driver. I/O operations are intercepted in the filter's
>> read/write coroutines, and referred to block/throttle-groups.c
>>
>> The driver can be used with the command
>> -drive driver=throttle,file.filename=foo.qcow2,iops-total=...
>> The configuration flags and semantics are identical to the hardcoded
>> throttling ones.
>>
>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>> ---
>>  block/Makefile.objs             |   1 +
>>  block/throttle.c                | 427 ++++++++++++++++++++++++++++++++++++++++
>>  include/qemu/throttle-options.h |  60 ++++--
>>  3 files changed, 469 insertions(+), 19 deletions(-)
>>  create mode 100644 block/throttle.c
>>
>> diff --git a/block/Makefile.objs b/block/Makefile.objs
>> index ea955302c8..bb811a4d01 100644
>> --- a/block/Makefile.objs
>> +++ b/block/Makefile.objs
>> @@ -25,6 +25,7 @@ block-obj-y += accounting.o dirty-bitmap.o
>>  block-obj-y += write-threshold.o
>>  block-obj-y += backup.o
>>  block-obj-$(CONFIG_REPLICATION) += replication.o
>> +block-obj-y += throttle.o
>>
>>  block-obj-y += crypto.o
>>
>> diff --git a/block/throttle.c b/block/throttle.c
>> new file mode 100644
>> index 0000000000..0c17051161
>> --- /dev/null
>> +++ b/block/throttle.c
>> @@ -0,0 +1,427 @@
>> +/*
>> + * QEMU block throttling filter driver infrastructure
>> + *
>> + * Copyright (c) 2017 Manos Pitsidianakis
>> + *
>> + * 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/>.
>> + */
>
>Please consider using the LGPL. We're still hoping to turn the block
>layer into a library one day, and almost all code in it is licensed
>liberally (MIT or LGPL).
>
>> +#include "qemu/osdep.h"
>> +#include "block/throttle-groups.h"
>> +#include "qemu/throttle-options.h"
>> +#include "qapi/error.h"
>> +
>> +
>> +static QemuOptsList throttle_opts = {
>> +    .name = "throttle",
>> +    .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
>> +    .desc = {
>> +        {
>> +            .name = QEMU_OPT_IOPS_TOTAL,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "limit total I/O operations per second",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_READ,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "limit read operations per second",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_WRITE,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "limit write operations per second",
>> +        },{
>> +            .name = QEMU_OPT_BPS_TOTAL,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "limit total bytes per second",
>> +        },{
>> +            .name = QEMU_OPT_BPS_READ,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "limit read bytes per second",
>> +        },{
>> +            .name = QEMU_OPT_BPS_WRITE,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "limit write bytes per second",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_TOTAL_MAX,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "I/O operations burst",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_READ_MAX,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "I/O operations read burst",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_WRITE_MAX,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "I/O operations write burst",
>> +        },{
>> +            .name = QEMU_OPT_BPS_TOTAL_MAX,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "total bytes burst",
>> +        },{
>> +            .name = QEMU_OPT_BPS_READ_MAX,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "total bytes read burst",
>> +        },{
>> +            .name = QEMU_OPT_BPS_WRITE_MAX,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "total bytes write burst",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "length of the iopstotalmax burst period, in seconds",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_READ_MAX_LENGTH,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "length of the iopsreadmax burst period, in seconds",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "length of the iopswritemax burst period, in seconds",
>> +        },{
>> +            .name = QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "length of the bpstotalmax burst period, in seconds",
>> +        },{
>> +            .name = QEMU_OPT_BPS_READ_MAX_LENGTH,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "length of the bpsreadmax burst period, in seconds",
>> +        },{
>> +            .name = QEMU_OPT_BPS_WRITE_MAX_LENGTH,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "length of the bpswritemax burst period, in seconds",
>> +        },{
>> +            .name = QEMU_OPT_IOPS_SIZE,
>> +            .type = QEMU_OPT_NUMBER,
>> +            .help = "when limiting by iops max size of an I/O in bytes",
>> +        },
>> +        {
>> +            .name = QEMU_OPT_THROTTLE_GROUP_NAME,
>> +            .type = QEMU_OPT_STRING,
>> +            .help = "throttle group name",
>> +        },
>> +        { /* end of list */ }
>> +    },
>> +};
>> +
>> +/* Extract ThrottleConfig options. Assumes cfg is initialized and will be
>> + * checked for validity.
>> + */
>> +
>> +static void throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg)
>> +{
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL)) {
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ)) {
>> +        cfg->buckets[THROTTLE_BPS_READ].avg  =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE)) {
>> +        cfg->buckets[THROTTLE_BPS_WRITE].avg =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL)) {
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ)) {
>> +        cfg->buckets[THROTTLE_OPS_READ].avg =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE)) {
>> +        cfg->buckets[THROTTLE_OPS_WRITE].avg =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX)) {
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].max =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX)) {
>> +        cfg->buckets[THROTTLE_BPS_READ].max  =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX)) {
>> +        cfg->buckets[THROTTLE_BPS_WRITE].max =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX)) {
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].max =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX)) {
>> +        cfg->buckets[THROTTLE_OPS_READ].max =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX)) {
>> +        cfg->buckets[THROTTLE_OPS_WRITE].max =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX_LENGTH)) {
>> +        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
>> +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
>> +            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
>> +        cfg->buckets[THROTTLE_OPS_READ].burst_length =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
>> +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, QEMU_OPT_IOPS_SIZE)) {
>> +        cfg->op_size =
>> +            qemu_opt_get_number(opts, QEMU_OPT_IOPS_SIZE, 0);
>> +    }
>> +}
>> +
>> +static int throttle_configure_tgm(BlockDriverState *bs, ThrottleGroupMember *tgm,
>> +                                                    QDict *options, Error **errp)
>
>Both lines exceed 80 characters. The indentation is off, too: QDict on
>the second line should be aligned with BlockDriverState on the first
>one.
>
>> +{
>> +    int ret = 0;
>> +    ThrottleState *ts;
>> +    ThrottleTimers *tt;
>> +    ThrottleConfig cfg;
>> +    QemuOpts *opts = NULL;
>> +    const char *group_name = NULL;
>> +    Error *local_err = NULL;
>> +
>> +    opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);
>> +    if (local_err) {
>> +        error_propagate(errp, local_err);
>> +        return -EINVAL;
>> +    }
>> +    qemu_opts_absorb_qdict(opts, options, &local_err);
>> +    if (local_err) {
>> +        goto err;
>> +    }
>> +
>> +    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
>> +    if (!group_name) {
>> +        group_name = bdrv_get_device_or_node_name(bs);
>
>This is a legacy function, we should avoid adding new callers.
>
>Worse yet, if the group isn't specified, we want to create a new
>group internally just for this throttle node. But nothing stops the user
>from creating a group that is named like the device or node name of
>another throttle node, so we might end up attaching to an existing
>throttle group instead!
>
>I think throttle groups should be anonymous rather than having a default
>name if they are created implicitly.

Since we're moving groups to QOM we will need ids for each group. Can 
objects be anonymous?


>
>> +        if (!strlen(group_name)) {
>
>More efficiently written as if (!*group_name), but it should go away
>anyway when you address the above comment.
>
>> +            error_setg(&local_err,
>> +                       "A group name must be specified for this device.");
>
>You can directly set errp and avoid the error_propagate() later.
>
>> +            goto err;
>> +        }
>> +    }
>> +
>> +    tgm->aio_context = bdrv_get_aio_context(bs);
>> +    /* Register membership to group with name group_name */
>> +    throttle_group_register_tgm(tgm, group_name);
>> +
>> +    ts = tgm->throttle_state;
>> +    /* Copy previous configuration */
>> +    throttle_get_config(ts, &cfg);
>> +
>> +    /* Change limits if user has specified them */
>> +    throttle_extract_options(opts, &cfg);
>> +    if (!throttle_is_valid(&cfg, &local_err)) {
>
>Here errp can directly be used, too.
>
>You only need the &local_err idiom if you want to check whether an error
>was set (because the caller might pass errp == NULL and then you can't
>tell whether an error occurred or not).
>
>> +        throttle_group_unregister_tgm(tgm);
>> +        goto err;
>> +    }
>> +    tt = &tgm->throttle_timers;
>> +    /* Update group configuration */
>> +    throttle_config(ts, tt, &cfg);
>> +
>> +    qemu_co_queue_init(&tgm->throttled_reqs[0]);
>> +    qemu_co_queue_init(&tgm->throttled_reqs[1]);
>> +
>> +    goto fin;
>
>I prefer an explicit ret = 0 right here because ret tends to be used to
>store the return value for all kinds of functions. In this specific
>case, it's still 0 from the initialisation, but that's easy to break
>accidentally in a future patch.
>
>> +
>> +err:
>> +    error_propagate(errp, local_err);
>> +    ret = -EINVAL;
>> +fin:
>> +    qemu_opts_del(opts);
>> +    return ret;
>> +}
>> +
>> +static int throttle_open(BlockDriverState *bs, QDict *options,
>> +                            int flags, Error **errp)
>> +{
>> +    ThrottleGroupMember *tgm = bs->opaque;
>> +    Error *local_err = NULL;
>> +
>> +    bs->open_flags = flags;
>> +    bs->file = bdrv_open_child(NULL, options, "file",
>> +                                    bs, &child_file, false, &local_err);
>> +
>> +    if (local_err) {
>
>If you check bs->file == NULL instead, you can avoid local_err.
>
>> +        error_propagate(errp, local_err);
>> +        return -EINVAL;
>> +    }
>> +
>> +    qdict_flatten(options);
>
>Why do you need this? options should already be flattened.
>
>> +    return throttle_configure_tgm(bs, tgm, options, errp);
>> +}
>> +
>> +static void throttle_close(BlockDriverState *bs)
>> +{
>> +    ThrottleGroupMember *tgm = bs->opaque;
>> +    bdrv_drained_begin(bs);
>> +    throttle_group_unregister_tgm(tgm);
>> +    bdrv_drained_end(bs);
>> +    return;
>
>This is a useless return statement. I think I've seen compilers warn
>about it (and we use -Werror, so this would be a build failure on them).
>
>bdrv_drained_begin/end should be unnecessary in .bdrv_close
>implementations, we always drain all requests before calling the
>callback.
>
>> +}
>> +
>> +
>> +static int64_t throttle_getlength(BlockDriverState *bs)
>> +{
>> +    return bdrv_getlength(bs->file->bs);
>> +}
>> +
>> +
>> +static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, uint64_t offset,
>
>This line exceeds 80 characters.
>
>> +                                            uint64_t bytes, QEMUIOVector *qiov,
>> +                                            int flags)
>
>Indentation is off by one.
>
>> +{
>> +
>> +    ThrottleGroupMember *tgm = bs->opaque;
>> +    throttle_group_co_io_limits_intercept(tgm, bytes, false);
>> +
>> +    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
>> +}
>> +
>> +static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs, uint64_t offset,
>
>Another long line.
>
>> +                                            uint64_t bytes, QEMUIOVector *qiov,
>> +                                            int flags)
>> +{
>> +    ThrottleGroupMember *tgm = bs->opaque;
>> +    throttle_group_co_io_limits_intercept(tgm, bytes, true);
>> +
>> +    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
>> +}
>
>Kevin

Thanks a lot for the comments. (This was one of the first things I 
wrote, so this is why it is not very idiomatic)

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-28 15:22     ` Manos Pitsidianakis
@ 2017-06-28 15:36       ` Kevin Wolf
  2017-06-28 15:50         ` Manos Pitsidianakis
  0 siblings, 1 reply; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 15:36 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel, qemu-block, Stefan Hajnoczi,
	Alberto Garcia

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

Am 28.06.2017 um 17:22 hat Manos Pitsidianakis geschrieben:
> On Wed, Jun 28, 2017 at 04:40:12PM +0200, Kevin Wolf wrote:
> >Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
> >>block/throttle.c uses existing I/O throttle infrastructure inside a
> >>block filter driver. I/O operations are intercepted in the filter's
> >>read/write coroutines, and referred to block/throttle-groups.c
> >>
> >>The driver can be used with the command
> >>-drive driver=throttle,file.filename=foo.qcow2,iops-total=...
> >>The configuration flags and semantics are identical to the hardcoded
> >>throttling ones.
> >>
> >>Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> >>---
> >> block/Makefile.objs             |   1 +
> >> block/throttle.c                | 427 ++++++++++++++++++++++++++++++++++++++++
> >> include/qemu/throttle-options.h |  60 ++++--
> >> 3 files changed, 469 insertions(+), 19 deletions(-)
> >> create mode 100644 block/throttle.c
> >>
> >>diff --git a/block/Makefile.objs b/block/Makefile.objs
> >>index ea955302c8..bb811a4d01 100644
> >>--- a/block/Makefile.objs
> >>+++ b/block/Makefile.objs
> >>@@ -25,6 +25,7 @@ block-obj-y += accounting.o dirty-bitmap.o
> >> block-obj-y += write-threshold.o
> >> block-obj-y += backup.o
> >> block-obj-$(CONFIG_REPLICATION) += replication.o
> >>+block-obj-y += throttle.o
> >>
> >> block-obj-y += crypto.o
> >>
> >>diff --git a/block/throttle.c b/block/throttle.c
> >>new file mode 100644
> >>index 0000000000..0c17051161
> >>--- /dev/null
> >>+++ b/block/throttle.c
> >>@@ -0,0 +1,427 @@
> >>+/*
> >>+ * QEMU block throttling filter driver infrastructure
> >>+ *
> >>+ * Copyright (c) 2017 Manos Pitsidianakis
> >>+ *
> >>+ * 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/>.
> >>+ */
> >
> >Please consider using the LGPL. We're still hoping to turn the block
> >layer into a library one day, and almost all code in it is licensed
> >liberally (MIT or LGPL).
> >
> >>+#include "qemu/osdep.h"
> >>+#include "block/throttle-groups.h"
> >>+#include "qemu/throttle-options.h"
> >>+#include "qapi/error.h"
> >>+
> >>+
> >>+static QemuOptsList throttle_opts = {
> >>+    .name = "throttle",
> >>+    .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
> >>+    .desc = {
> >>+        {
> >>+            .name = QEMU_OPT_IOPS_TOTAL,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "limit total I/O operations per second",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_READ,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "limit read operations per second",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_WRITE,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "limit write operations per second",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_TOTAL,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "limit total bytes per second",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_READ,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "limit read bytes per second",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_WRITE,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "limit write bytes per second",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_TOTAL_MAX,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "I/O operations burst",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_READ_MAX,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "I/O operations read burst",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_WRITE_MAX,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "I/O operations write burst",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_TOTAL_MAX,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "total bytes burst",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_READ_MAX,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "total bytes read burst",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_WRITE_MAX,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "total bytes write burst",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "length of the iopstotalmax burst period, in seconds",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_READ_MAX_LENGTH,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "length of the iopsreadmax burst period, in seconds",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "length of the iopswritemax burst period, in seconds",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "length of the bpstotalmax burst period, in seconds",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_READ_MAX_LENGTH,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "length of the bpsreadmax burst period, in seconds",
> >>+        },{
> >>+            .name = QEMU_OPT_BPS_WRITE_MAX_LENGTH,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "length of the bpswritemax burst period, in seconds",
> >>+        },{
> >>+            .name = QEMU_OPT_IOPS_SIZE,
> >>+            .type = QEMU_OPT_NUMBER,
> >>+            .help = "when limiting by iops max size of an I/O in bytes",
> >>+        },
> >>+        {
> >>+            .name = QEMU_OPT_THROTTLE_GROUP_NAME,
> >>+            .type = QEMU_OPT_STRING,
> >>+            .help = "throttle group name",
> >>+        },
> >>+        { /* end of list */ }
> >>+    },
> >>+};
> >>+
> >>+/* Extract ThrottleConfig options. Assumes cfg is initialized and will be
> >>+ * checked for validity.
> >>+ */
> >>+
> >>+static void throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg)
> >>+{
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL)) {
> >>+        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ)) {
> >>+        cfg->buckets[THROTTLE_BPS_READ].avg  =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE)) {
> >>+        cfg->buckets[THROTTLE_BPS_WRITE].avg =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL)) {
> >>+        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ)) {
> >>+        cfg->buckets[THROTTLE_OPS_READ].avg =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE)) {
> >>+        cfg->buckets[THROTTLE_OPS_WRITE].avg =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX)) {
> >>+        cfg->buckets[THROTTLE_BPS_TOTAL].max =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX)) {
> >>+        cfg->buckets[THROTTLE_BPS_READ].max  =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX)) {
> >>+        cfg->buckets[THROTTLE_BPS_WRITE].max =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX)) {
> >>+        cfg->buckets[THROTTLE_OPS_TOTAL].max =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX)) {
> >>+        cfg->buckets[THROTTLE_OPS_READ].max =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX)) {
> >>+        cfg->buckets[THROTTLE_OPS_WRITE].max =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX, 0);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
> >>+        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_READ_MAX_LENGTH)) {
> >>+        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
> >>+        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
> >>+        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
> >>+        cfg->buckets[THROTTLE_OPS_READ].burst_length =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
> >>+        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
> >>+    }
> >>+    if (qemu_opt_get(opts, QEMU_OPT_IOPS_SIZE)) {
> >>+        cfg->op_size =
> >>+            qemu_opt_get_number(opts, QEMU_OPT_IOPS_SIZE, 0);
> >>+    }
> >>+}
> >>+
> >>+static int throttle_configure_tgm(BlockDriverState *bs, ThrottleGroupMember *tgm,
> >>+                                                    QDict *options, Error **errp)
> >
> >Both lines exceed 80 characters. The indentation is off, too: QDict on
> >the second line should be aligned with BlockDriverState on the first
> >one.
> >
> >>+{
> >>+    int ret = 0;
> >>+    ThrottleState *ts;
> >>+    ThrottleTimers *tt;
> >>+    ThrottleConfig cfg;
> >>+    QemuOpts *opts = NULL;
> >>+    const char *group_name = NULL;
> >>+    Error *local_err = NULL;
> >>+
> >>+    opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);
> >>+    if (local_err) {
> >>+        error_propagate(errp, local_err);
> >>+        return -EINVAL;
> >>+    }
> >>+    qemu_opts_absorb_qdict(opts, options, &local_err);
> >>+    if (local_err) {
> >>+        goto err;
> >>+    }
> >>+
> >>+    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
> >>+    if (!group_name) {
> >>+        group_name = bdrv_get_device_or_node_name(bs);
> >
> >This is a legacy function, we should avoid adding new callers.
> >
> >Worse yet, if the group isn't specified, we want to create a new
> >group internally just for this throttle node. But nothing stops the user
> >from creating a group that is named like the device or node name of
> >another throttle node, so we might end up attaching to an existing
> >throttle group instead!
> >
> >I think throttle groups should be anonymous rather than having a default
> >name if they are created implicitly.
> 
> Since we're moving groups to QOM we will need ids for each group.
> Can objects be anonymous?

Hm, that's a good question. But object_new() doesn't take an ID, so I
think they can be anonymous.

Looking a bit closer, strcut Object doesn't even have a field for the
ID. It seems that what the ID really is is the name of a property in a
parent object that points to the new object. So as long as you don't
want to have another QOM object point to it, there is no such thing as
an ID.

Anyway, I followed the call chain from throttle_group_register_tgm() and
it ends in throttle_group_incref() where we simply have this:

    tg = g_new0(ThrottleGroup, 1);
    tg->name = g_strdup(name);

Shouldn't this be using something a little more QOMy now?

Kevin

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
                     ` (2 preceding siblings ...)
  2017-06-28 13:35   ` Alberto Garcia
@ 2017-06-28 15:50   ` Kevin Wolf
  2017-06-28 16:02     ` Eric Blake
  3 siblings, 1 reply; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 15:50 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Alberto Garcia

Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
> This is needed to configure throttle filter driver nodes with QAPI.
> 
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
>  qapi/block-core.json | 19 ++++++++++++++++++-
>  1 file changed, 18 insertions(+), 1 deletion(-)
> 
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index f85c2235c7..1d4afafe8c 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -2119,7 +2119,7 @@
>              'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
>              'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
>              'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
> -            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
> +            'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
>  
>  ##
>  # @BlockdevOptionsFile:
> @@ -2984,6 +2984,7 @@
>        'replication':'BlockdevOptionsReplication',
>        'sheepdog':   'BlockdevOptionsSheepdog',
>        'ssh':        'BlockdevOptionsSsh',
> +      'throttle':   'BlockdevOptionsThrottle',
>        'vdi':        'BlockdevOptionsGenericFormat',
>        'vhdx':       'BlockdevOptionsGenericFormat',
>        'vmdk':       'BlockdevOptionsGenericCOWFormat',
> @@ -3723,3 +3724,19 @@
>    'data' : { 'parent': 'str',
>               '*child': 'str',
>               '*node': 'str' } }
> +
> +##
> +# @BlockdevOptionsThrottle:
> +#
> +# Driver specific block device options for Throttle
> +#
> +# @throttling-group: the name of the throttling group to use
> +#
> +# @options:        BlockIOThrottle options

Missing #optional marker.

> +# Since: 2.9
> +##
> +{ 'struct': 'BlockdevOptionsThrottle',
> +  'data': { 'throttling-group': 'str',
> +            'file' : 'BlockdevRef',
> +            '*options' : 'BlockIOThrottle'
> +             } }

Didn't we intend to make 'throttling-group' optional, too?

If we don't, then the question of anonymous ThrottleGroup objects is
kind of moot (not completely because -drive isn't bound to the schema,
but in that case we should just error out there too if it's missing).

Kevin

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

* Re: [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver
  2017-06-28 15:36       ` Kevin Wolf
@ 2017-06-28 15:50         ` Manos Pitsidianakis
  0 siblings, 0 replies; 55+ messages in thread
From: Manos Pitsidianakis @ 2017-06-28 15:50 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, qemu-block, Stefan Hajnoczi, Alberto Garcia

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

On Wed, Jun 28, 2017 at 05:36:54PM +0200, Kevin Wolf wrote:
>Am 28.06.2017 um 17:22 hat Manos Pitsidianakis geschrieben:
>> Since we're moving groups to QOM we will need ids for each group.
>> Can objects be anonymous?
>
>Hm, that's a good question. But object_new() doesn't take an ID, so I
>think they can be anonymous.
>
>Looking a bit closer, strcut Object doesn't even have a field for the
>ID. It seems that what the ID really is is the name of a property in a
>parent object that points to the new object. So as long as you don't
>want to have another QOM object point to it, there is no such thing as
>an ID.
>
>Anyway, I followed the call chain from throttle_group_register_tgm() and
>it ends in throttle_group_incref() where we simply have this:
>
>    tg = g_new0(ThrottleGroup, 1);
>    tg->name = g_strdup(name);
>
>Shouldn't this be using something a little more QOMy now?

Yes, like it was mentioned in the QOM patch's thread 
block/throttle-groups.c should use QOM internally. I will change this 
for the next revision and also look into anonymity.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver()
  2017-06-26 15:11   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-06-28 15:55     ` Kevin Wolf
  0 siblings, 0 replies; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 15:55 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Manos Pitsidianakis, qemu-devel, Stefan Hajnoczi, qemu-block

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

Am 26.06.2017 um 17:11 hat Stefan Hajnoczi geschrieben:
> On Fri, Jun 23, 2017 at 03:46:58PM +0300, Manos Pitsidianakis wrote:
> > diff --git a/block.c b/block.c
> > index 694396281b..c7d9f8959a 100644
> > --- a/block.c
> > +++ b/block.c
> > @@ -1150,20 +1150,25 @@ free_and_fail:
> >  }
> >  
> >  BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
> > -                                       int flags, Error **errp)
> > +                                       int flags, QDict *options, Error **errp)
> 
> Please add a doc comment that explains the QDict ownership when options
> != NULL.  Users need to understand whether the options QDict still
> belongs to them after the call or bdrv_new_open_driver() takes over
> ownership.
> 
> See bdrv_open_inherit() for an example.

I think we might not only want to document it, but probably also change
this function. bdrv_open_inherit() and friends take ownership of the
QDict, so doing the same here would probably avoid bugs in the future.

It also seems more practical, because the only user that is added in
patch 8 already does QDECREF() after calling bdrv_new_open_driver().

Kevin

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-28 15:50   ` Kevin Wolf
@ 2017-06-28 16:02     ` Eric Blake
  2017-06-28 16:18       ` Kevin Wolf
  0 siblings, 1 reply; 55+ messages in thread
From: Eric Blake @ 2017-06-28 16:02 UTC (permalink / raw)
  To: Kevin Wolf, Manos Pitsidianakis
  Cc: Alberto Garcia, Stefan Hajnoczi, qemu-devel, qemu-block

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

On 06/28/2017 10:50 AM, Kevin Wolf wrote:
> Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
>> This is needed to configure throttle filter driver nodes with QAPI.
>>
>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>> ---
>>  qapi/block-core.json | 19 ++++++++++++++++++-
>>  1 file changed, 18 insertions(+), 1 deletion(-)
>>
>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>> index f85c2235c7..1d4afafe8c 100644
>> --- a/qapi/block-core.json
>> +++ b/qapi/block-core.json
>> @@ -2119,7 +2119,7 @@
>>              'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
>>              'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
>>              'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
>> -            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
>> +            'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
>>  
>>  ##
>>  # @BlockdevOptionsFile:
>> @@ -2984,6 +2984,7 @@
>>        'replication':'BlockdevOptionsReplication',
>>        'sheepdog':   'BlockdevOptionsSheepdog',
>>        'ssh':        'BlockdevOptionsSsh',
>> +      'throttle':   'BlockdevOptionsThrottle',
>>        'vdi':        'BlockdevOptionsGenericFormat',
>>        'vhdx':       'BlockdevOptionsGenericFormat',
>>        'vmdk':       'BlockdevOptionsGenericCOWFormat',
>> @@ -3723,3 +3724,19 @@
>>    'data' : { 'parent': 'str',
>>               '*child': 'str',
>>               '*node': 'str' } }
>> +
>> +##
>> +# @BlockdevOptionsThrottle:
>> +#
>> +# Driver specific block device options for Throttle
>> +#
>> +# @throttling-group: the name of the throttling group to use
>> +#
>> +# @options:        BlockIOThrottle options
> 
> Missing #optional marker.

The marker is now auto-generated based solely on the '*options' below,
so we don't need a redundant thing here.

> 
>> +# Since: 2.9
>> +##
>> +{ 'struct': 'BlockdevOptionsThrottle',
>> +  'data': { 'throttling-group': 'str',
>> +            'file' : 'BlockdevRef',
>> +            '*options' : 'BlockIOThrottle'
>> +             } }
> 
> Didn't we intend to make 'throttling-group' optional, too?
> 
> If we don't, then the question of anonymous ThrottleGroup objects is
> kind of moot (not completely because -drive isn't bound to the schema,
> but in that case we should just error out there too if it's missing).
> 
> Kevin
> 
> 

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


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

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

* Re: [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI
  2017-06-28 16:02     ` Eric Blake
@ 2017-06-28 16:18       ` Kevin Wolf
  0 siblings, 0 replies; 55+ messages in thread
From: Kevin Wolf @ 2017-06-28 16:18 UTC (permalink / raw)
  To: Eric Blake
  Cc: Manos Pitsidianakis, Alberto Garcia, Stefan Hajnoczi, qemu-devel,
	qemu-block

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

Am 28.06.2017 um 18:02 hat Eric Blake geschrieben:
> On 06/28/2017 10:50 AM, Kevin Wolf wrote:
> > Am 23.06.2017 um 14:46 hat Manos Pitsidianakis geschrieben:
> >> This is needed to configure throttle filter driver nodes with QAPI.
> >>
> >> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> >> ---
> >>  qapi/block-core.json | 19 ++++++++++++++++++-
> >>  1 file changed, 18 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/qapi/block-core.json b/qapi/block-core.json
> >> index f85c2235c7..1d4afafe8c 100644
> >> --- a/qapi/block-core.json
> >> +++ b/qapi/block-core.json
> >> @@ -2119,7 +2119,7 @@
> >>              'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
> >>              'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
> >>              'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
> >> -            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
> >> +            'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
> >>  
> >>  ##
> >>  # @BlockdevOptionsFile:
> >> @@ -2984,6 +2984,7 @@
> >>        'replication':'BlockdevOptionsReplication',
> >>        'sheepdog':   'BlockdevOptionsSheepdog',
> >>        'ssh':        'BlockdevOptionsSsh',
> >> +      'throttle':   'BlockdevOptionsThrottle',
> >>        'vdi':        'BlockdevOptionsGenericFormat',
> >>        'vhdx':       'BlockdevOptionsGenericFormat',
> >>        'vmdk':       'BlockdevOptionsGenericCOWFormat',
> >> @@ -3723,3 +3724,19 @@
> >>    'data' : { 'parent': 'str',
> >>               '*child': 'str',
> >>               '*node': 'str' } }
> >> +
> >> +##
> >> +# @BlockdevOptionsThrottle:
> >> +#
> >> +# Driver specific block device options for Throttle
> >> +#
> >> +# @throttling-group: the name of the throttling group to use
> >> +#
> >> +# @options:        BlockIOThrottle options
> > 
> > Missing #optional marker.
> 
> The marker is now auto-generated based solely on the '*options' below,
> so we don't need a redundant thing here.

Oh nice, progress!

Kevin

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

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

end of thread, other threads:[~2017-06-28 16:18 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-23 12:46 [Qemu-devel] [PATCH RFC v3 0/8] I/O Throtting block filter driver Manos Pitsidianakis
2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 1/8] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
2017-06-26 13:23   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-06-27 12:08   ` [Qemu-devel] " Alberto Garcia
2017-06-27 12:24     ` Manos Pitsidianakis
2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 2/8] block: Add aio_context field in ThrottleGroupMember Manos Pitsidianakis
2017-06-26 13:36   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-06-26 14:03     ` Manos Pitsidianakis
2017-06-27 12:39   ` [Qemu-devel] " Alberto Garcia
2017-06-28 11:27   ` Kevin Wolf
2017-06-28 12:15     ` Manos Pitsidianakis
2017-06-28 12:44       ` Kevin Wolf
2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 3/8] block: add throttle block filter driver Manos Pitsidianakis
2017-06-26 14:00   ` [Qemu-devel] [Qemu-block] " Manos Pitsidianakis
2017-06-26 14:30   ` Stefan Hajnoczi
2017-06-26 16:01     ` Manos Pitsidianakis
2017-06-27 12:42       ` Stefan Hajnoczi
2017-06-26 16:26     ` Manos Pitsidianakis
2017-06-27 12:45       ` Stefan Hajnoczi
2017-06-27 13:34         ` Manos Pitsidianakis
2017-06-28 12:11           ` Stefan Hajnoczi
2017-06-26 14:34   ` Stefan Hajnoczi
2017-06-28 14:40   ` [Qemu-devel] " Kevin Wolf
2017-06-28 15:22     ` Manos Pitsidianakis
2017-06-28 15:36       ` Kevin Wolf
2017-06-28 15:50         ` Manos Pitsidianakis
2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 4/8] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
2017-06-26 14:52   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-06-26 15:24     ` Manos Pitsidianakis
2017-06-27 12:57       ` Stefan Hajnoczi
2017-06-26 16:58     ` Manos Pitsidianakis
2017-06-27 13:02       ` Stefan Hajnoczi
2017-06-27 16:05       ` Alberto Garcia
2017-06-27 16:12         ` Manos Pitsidianakis
2017-06-28 12:07         ` Stefan Hajnoczi
2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 5/8] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
2017-06-26 14:55   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-06-27 13:12   ` [Qemu-devel] " Eric Blake
2017-06-28 13:35   ` Alberto Garcia
2017-06-28 13:42     ` Manos Pitsidianakis
2017-06-28 15:50   ` Kevin Wolf
2017-06-28 16:02     ` Eric Blake
2017-06-28 16:18       ` Kevin Wolf
2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 6/8] block: add options parameter to bdrv_new_open_driver() Manos Pitsidianakis
2017-06-26 15:11   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-06-28 15:55     ` Kevin Wolf
2017-06-28 13:42   ` [Qemu-devel] " Alberto Garcia
2017-06-28 13:47     ` Manos Pitsidianakis
2017-06-23 12:46 ` [Qemu-devel] [PATCH RFC v3 7/8] block: remove legacy I/O throttling Manos Pitsidianakis
2017-06-26 15:44   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-06-26 22:45     ` Manos Pitsidianakis
2017-06-27 13:08       ` Stefan Hajnoczi
2017-06-23 12:47 ` [Qemu-devel] [PATCH RFC v3 8/8] block: add throttle block filter driver interface tests Manos Pitsidianakis
2017-06-28 11:18   ` Kevin Wolf
2017-06-26 15:46 ` [Qemu-devel] [Qemu-block] [PATCH RFC v3 0/8] I/O Throtting block filter driver Stefan Hajnoczi

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.