All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter
@ 2017-07-31  9:54 Manos Pitsidianakis
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
                   ` (6 more replies)
  0 siblings, 7 replies; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

This series adds a throttle block driver filter. Currently throttling is done
at the BlockBackend level. Using block driver interfaces we can move the
throttling to any point in the BDS graph using a throttle node which uses the
existing throttling code. This allows for potentially more complex
configurations (throttling at any point in the graph, chained filters)

There's also the question of how filter drivers affect the rest of the block
layer, especially live block jobs. This won't be tackled in this series though.

v3:
  fix style error in 'add aio_context field in ThrottleGroupMember'

v2:
  change QOM throttle group object name
  print valid ranges for uint on error
  move frees in throttle_group_obj_finalize()
  split throttle_group_{set,get}()
  add throttle_recurse_is_first_non_filter()

Manos Pitsidianakis (7):
  block: move ThrottleGroup membership to ThrottleGroupMember
  block: add aio_context field in ThrottleGroupMember
  block: tidy ThrottleGroupMember initializations
  block: convert ThrottleGroup to object with QOM
  block: add throttle block filter driver
  block: add BlockDevOptionsThrottle to QAPI
  block: add throttle block filter driver interface tests

 block/Makefile.objs             |   1 +
 block/block-backend.c           |  62 ++--
 block/qapi.c                    |   8 +-
 block/throttle-groups.c         | 715 ++++++++++++++++++++++++++++++----------
 block/throttle.c                | 395 ++++++++++++++++++++++
 blockdev.c                      |   4 +-
 include/block/throttle-groups.h |  47 ++-
 include/qemu/throttle-options.h |  60 ++--
 include/qemu/throttle.h         |   3 +
 include/sysemu/block-backend.h  |  20 +-
 qapi/block-core.json            |  68 +++-
 tests/qemu-iotests/184          | 237 +++++++++++++
 tests/qemu-iotests/184.out      | 319 ++++++++++++++++++
 tests/qemu-iotests/group        |   1 +
 tests/test-throttle.c           | 111 ++++---
 util/throttle.c                 | 151 +++++++++
 16 files changed, 1892 insertions(+), 310 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] 43+ messages in thread

* [Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember
  2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
@ 2017-07-31  9:54 ` Manos Pitsidianakis
  2017-08-04 11:59   ` Alberto Garcia
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 2/7] block: add aio_context field in ThrottleGroupMember Manos Pitsidianakis
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

This commit 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. This is done by gathering ThrottleGroup membership
details from BlockBackendPublic into ThrottleGroupMember and refactoring
existing code to use the structure.

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/block-backend.c           |  66 +++++----
 block/qapi.c                    |   8 +-
 block/throttle-groups.c         | 288 ++++++++++++++++++++--------------------
 blockdev.c                      |   4 +-
 include/block/throttle-groups.h |  39 +++++-
 include/sysemu/block-backend.h  |  20 +--
 tests/test-throttle.c           |  53 ++++----
 7 files changed, 252 insertions(+), 226 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index 968438c149..bde6948d0e 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -215,9 +215,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);
@@ -285,7 +285,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) {
@@ -596,9 +596,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);
@@ -620,9 +623,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;
@@ -984,8 +988,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);
@@ -1008,10 +1013,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) {
@@ -1680,15 +1685,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 d2b18ee9df..8d18920ae1 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -66,10 +66,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
 
     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;
@@ -117,7 +118,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 890bfded3f..c8ed16ddf8 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,26 +40,26 @@
  * 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
+ * ThrottleGroupMember is registered in a group those fields can be accessed
  * by other threads any time.
  *
  * Again, all this is handled internally and is mostly transparent to
  * the outside. The 'throttle_timers' field however has an additional
  * constraint because it may be temporarily invalid (see for example
  * blk_set_aio_context()). Therefore in this file a thread will
- * access some other BlockBackend's timers only after verifying that
- * that BlockBackend has throttled requests in the queue.
+ * access some other ThrottleGroupMember's timers only after verifying that
+ * that ThrottleGroupMember has throttled requests in the queue.
  */
 typedef struct ThrottleGroup {
     char *name; /* This is constant during the lifetime of the group */
 
     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];
     QEMUClockType clock_type;
 
@@ -140,114 +140,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 got 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;
     }
 
@@ -258,30 +256,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;
 }
@@ -290,19 +287,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;
     }
 
@@ -311,12 +308,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(tg->clock_type);
             timer_mod(tt->timers[is_write], now);
             tg->any_timer_armed[is_write] = true;
@@ -329,76 +326,77 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
  * 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
     };
 
@@ -406,13 +404,11 @@ 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);
     }
 }
 
@@ -420,32 +416,30 @@ void throttle_group_restart_blk(BlockBackend *blk)
  * to throttle_config(), but guarantees atomicity within the
  * throttling group.
  *
- * @blk: a BlockBackend that is a member of the group
+ * @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);
-    ThrottleState *ts = blkp->throttle_state;
+    ThrottleState *ts = tgm->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     qemu_mutex_lock(&tg->lock);
     throttle_config(ts, tg->clock_type, 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.
  *
- * @blk: a BlockBackend that is a member of the group
+ * @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);
@@ -461,7 +455,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 */
@@ -470,7 +465,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)
@@ -483,32 +478,36 @@ 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);
-    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),
                          tg->clock_type,
                          read_timer_cb,
@@ -518,45 +517,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 02cd69bc21..23475abb72 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2688,7 +2688,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 :
@@ -2698,7 +2698,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..1a6bcdae74 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -28,19 +28,44 @@
 #include "qemu/throttle.h"
 #include "block/block_int.h"
 
-const char *throttle_group_get_name(BlockBackend *blk);
+/* 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;
+
+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/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 4a3730596b..0e0cda7521 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 768f11dfed..6e6d926883 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] 43+ messages in thread

* [Qemu-devel] [PATCH v3 2/7] block: add aio_context field in ThrottleGroupMember
  2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
@ 2017-07-31  9:54 ` Manos Pitsidianakis
  2017-08-04 12:14   ` Alberto Garcia
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 3/7] block: tidy ThrottleGroupMember initializations Manos Pitsidianakis
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

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.

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/block-backend.c           | 15 +++++-----
 block/throttle-groups.c         | 38 ++++++++++++++++---------
 include/block/throttle-groups.h |  7 ++++-
 tests/test-throttle.c           | 63 +++++++++++++++++++++--------------------
 4 files changed, 70 insertions(+), 53 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index bde6948d0e..6687a90660 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1685,17 +1685,15 @@ 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;
+    ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
 
     if (bs) {
-        if (blk->public.throttle_group_member.throttle_state) {
-            tt = &blk->public.throttle_group_member.throttle_timers;
-            throttle_timers_detach_aio_context(tt);
+        if (tgm->throttle_state) {
+            throttle_group_detach_aio_context(tgm);
         }
         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);
+        if (tgm->throttle_state) {
+            throttle_group_attach_aio_context(tgm, new_context);
         }
     }
 }
@@ -1929,7 +1927,8 @@ void blk_io_limits_disable(BlockBackend *blk)
 void blk_io_limits_enable(BlockBackend *blk, const char *group)
 {
     assert(!blk->public.throttle_group_member.throttle_state);
-    throttle_group_register_tgm(&blk->public.throttle_group_member, group);
+    throttle_group_register_tgm(&blk->public.throttle_group_member,
+                                group, blk_get_aio_context(blk));
 }
 
 void blk_io_limits_update_group(BlockBackend *blk, const char *group)
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index c8ed16ddf8..a979e86243 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -391,9 +391,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,
@@ -401,7 +398,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)
@@ -449,13 +446,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);
 
@@ -484,18 +479,18 @@ static void write_timer_cb(void *opaque)
  *
  * @tgm:       the ThrottleGroupMember to insert
  * @groupname: the name of the group
+ * @ctx:       the AioContext to use
  */
 void throttle_group_register_tgm(ThrottleGroupMember *tgm,
-                                 const char *groupname)
+                                 const char *groupname,
+                                 AioContext *ctx)
 {
     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);
 
     tgm->throttle_state = ts;
+    tgm->aio_context = ctx;
 
     qemu_mutex_lock(&tg->lock);
     /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
@@ -508,11 +503,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,
                          tg->clock_type,
                          read_timer_cb,
                          write_timer_cb,
-                         blk);
+                         tgm);
 
     qemu_mutex_unlock(&tg->lock);
 }
@@ -559,6 +554,21 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
     tgm->throttle_state = NULL;
 }
 
+void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
+                                       AioContext *new_context)
+{
+    ThrottleTimers *tt = &tgm->throttle_timers;
+    throttle_timers_attach_aio_context(tt, new_context);
+    tgm->aio_context = new_context;
+}
+
+void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
+{
+    tgm->aio_context = NULL;
+    ThrottleTimers *tt = &tgm->throttle_timers;
+    throttle_timers_detach_aio_context(tt);
+}
+
 static void throttle_groups_init(void)
 {
     qemu_mutex_init(&throttle_groups_lock);
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index 1a6bcdae74..a0f27cac63 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -33,6 +33,7 @@
  */
 
 typedef struct ThrottleGroupMember {
+    AioContext   *aio_context;
     /* throttled_reqs_lock protects the CoQueues for throttled requests.  */
     CoMutex      throttled_reqs_lock;
     CoQueue      throttled_reqs[2];
@@ -61,12 +62,16 @@ void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
 void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
 
 void throttle_group_register_tgm(ThrottleGroupMember *tgm,
-                                const char *groupname);
+                                const char *groupname,
+                                AioContext *ctx);
 void throttle_group_unregister_tgm(ThrottleGroupMember *tgm);
 void throttle_group_restart_tgm(ThrottleGroupMember *tgm);
 
 void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
                                                         unsigned int bytes,
                                                         bool is_write);
+void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
+                                       AioContext *new_context);
+void throttle_group_detach_aio_context(ThrottleGroupMember *tgm);
 
 #endif
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 6e6d926883..57cf5ba711 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,7 +227,7 @@ 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);
@@ -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,7 +487,7 @@ 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, QEMU_CLOCK_VIRTUAL, &cfg);
 
@@ -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;
 }
@@ -611,9 +614,9 @@ static void test_groups(void)
     g_assert(tgm2->throttle_state == NULL);
     g_assert(tgm3->throttle_state == NULL);
 
-    throttle_group_register_tgm(tgm1, "bar");
-    throttle_group_register_tgm(tgm2, "foo");
-    throttle_group_register_tgm(tgm3, "bar");
+    throttle_group_register_tgm(tgm1, "bar", blk_get_aio_context(blk1));
+    throttle_group_register_tgm(tgm2, "foo", blk_get_aio_context(blk2));
+    throttle_group_register_tgm(tgm3, "bar", blk_get_aio_context(blk3));
 
     g_assert(tgm1->throttle_state != NULL);
     g_assert(tgm2->throttle_state != NULL);
-- 
2.11.0

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

* [Qemu-devel] [PATCH v3 3/7] block: tidy ThrottleGroupMember initializations
  2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 2/7] block: add aio_context field in ThrottleGroupMember Manos Pitsidianakis
@ 2017-07-31  9:54 ` Manos Pitsidianakis
  2017-08-04 12:35   ` Alberto Garcia
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

Move the CoMutex and CoQueue inits inside throttle_group_register_tgm()
which is called whenever a ThrottleGroupMember is initialized. There's
no need for them to be separate.

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 block/block-backend.c   | 3 ---
 block/throttle-groups.c | 3 +++
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index 6687a90660..df0200fc49 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -215,9 +215,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);
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index a979e86243..f711a3dc62 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -508,6 +508,9 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
                          read_timer_cb,
                          write_timer_cb,
                          tgm);
+    qemu_co_mutex_init(&tgm->throttled_reqs_lock);
+    qemu_co_queue_init(&tgm->throttled_reqs[0]);
+    qemu_co_queue_init(&tgm->throttled_reqs[1]);
 
     qemu_mutex_unlock(&tg->lock);
 }
-- 
2.11.0

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

* [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
                   ` (2 preceding siblings ...)
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 3/7] block: tidy ThrottleGroupMember initializations Manos Pitsidianakis
@ 2017-07-31  9:54 ` Manos Pitsidianakis
  2017-08-01 15:47   ` Stefan Hajnoczi
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver Manos Pitsidianakis
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

ThrottleGroup is converted to an object. This will allow the future
throttle block filter drive easy creation and configuration of throttle
groups in QMP and cli.

A new QAPI struct, ThrottleLimits, is introduced to provide a shared
struct for all throttle configuration needs in QMP.

ThrottleGroups can be created via CLI as
    -object throttle-group,id=foo,x-iops-total=100,x-..
where x-* are individual limit properties. Since we can't add non-scalar
properties in -object this interface must be used instead. However,
setting these properties must be disabled after initialization because
certain combinations of limits are forbidden and thus configuration
changes should be done in one transaction. The individual properties
will go away when support for non-scalar values in CLI is implemented
and thus are marked as experimental.

ThrottleGroup also has a `limits` property that uses the ThrottleLimits
struct.  It can be used to create ThrottleGroups or set the
configuration in existing groups as follows:

{ "execute": "object-add",
  "arguments": {
    "qom-type": "throttle-group",
    "id": "foo",
    "props" : {
      "limits": {
          "iops-total": 100
      }
    }
  }
}
{ "execute" : "qom-set",
    "arguments" : {
        "path" : "foo",
        "property" : "limits",
        "value" : {
            "iops-total" : 99
        }
    }
}

This also means a group's configuration can be fetched with qom-get.

ThrottleGroups can be anonymous which means they can't get accessed by
other users ie they will always be units instead of group (Because they
have one ThrottleGroupMember).

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
Notes:
    Note: I tested Markus Armbruster's patch in <87wp7fghi9.fsf@dusky.pond.sub.org>
    on master and I can use this syntax successfuly:
    -object '{ "qom-type" : "throttle-group", "id" : "foo", "props" : { "limits" \
    : { "iops-total" : 1000 } } }'
    If this gets merged using -object will be a little more verbose but at least we
    won't have seperate properties, which is a good thing, so the x-* should be
    dropped.

 block/throttle-groups.c         | 402 ++++++++++++++++++++++++++++++++++++----
 include/block/throttle-groups.h |   3 +
 include/qemu/throttle-options.h |  59 ++++--
 include/qemu/throttle.h         |   3 +
 qapi/block-core.json            |  48 +++++
 tests/test-throttle.c           |   1 +
 util/throttle.c                 | 151 +++++++++++++++
 7 files changed, 617 insertions(+), 50 deletions(-)

diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index f711a3dc62..b9c5036e44 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -25,9 +25,17 @@
 #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"
+#include "qapi-visit.h"
+#include "qom/object.h"
+#include "qom/object_interfaces.h"
+
+static void throttle_group_obj_init(Object *obj);
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
 
 /* The ThrottleGroup structure (with its ThrottleState) is shared
  * among different ThrottleGroupMembers and it's independent from
@@ -54,6 +62,10 @@
  * that ThrottleGroupMember has throttled requests in the queue.
  */
 typedef struct ThrottleGroup {
+    Object parent_obj;
+
+    /* refuse individual property change if initialization is complete */
+    bool is_initialized;
     char *name; /* This is constant during the lifetime of the group */
 
     QemuMutex lock; /* This lock protects the following four fields */
@@ -63,8 +75,7 @@ typedef struct ThrottleGroup {
     bool any_timer_armed[2];
     QEMUClockType clock_type;
 
-    /* These two are protected by the global throttle_groups_lock */
-    unsigned refcount;
+    /* This is protected by the global throttle_groups_lock */
     QTAILQ_ENTRY(ThrottleGroup) list;
 } ThrottleGroup;
 
@@ -77,7 +88,8 @@ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
  * If no ThrottleGroup is found with the given name a new one is
  * created.
  *
- * @name: the name of the ThrottleGroup
+ * @name: the name of the ThrottleGroup, NULL means a new anonymous group will
+ *        be created.
  * @ret:  the ThrottleState member of the ThrottleGroup
  */
 ThrottleState *throttle_group_incref(const char *name)
@@ -87,32 +99,30 @@ ThrottleState *throttle_group_incref(const char *name)
 
     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)) {
-            tg = iter;
-            break;
+    if (name) {
+        /* Look for an existing group with that name */
+        QTAILQ_FOREACH(iter, &throttle_groups, list) {
+            if (!g_strcmp0(name, iter->name)) {
+                tg = iter;
+                break;
+            }
         }
     }
 
     /* Create a new one if not found */
     if (!tg) {
-        tg = g_new0(ThrottleGroup, 1);
+        /* new ThrottleGroup obj will have a refcnt = 1 */
+        tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
         tg->name = g_strdup(name);
-        tg->clock_type = QEMU_CLOCK_REALTIME;
-
-        if (qtest_enabled()) {
-            /* For testing block IO throttling only */
-            tg->clock_type = QEMU_CLOCK_VIRTUAL;
-        }
-        qemu_mutex_init(&tg->lock);
-        throttle_init(&tg->ts);
-        QLIST_INIT(&tg->head);
-
-        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+        throttle_group_obj_complete((UserCreatable *)tg, &error_abort);
     }
 
-    tg->refcount++;
+    qemu_mutex_lock(&tg->lock);
+    if (!QLIST_EMPTY(&tg->head)) {
+        /* only ref if the group is not empty */
+        object_ref(OBJECT(tg));
+    }
+    qemu_mutex_unlock(&tg->lock);
 
     qemu_mutex_unlock(&throttle_groups_lock);
 
@@ -129,15 +139,7 @@ ThrottleState *throttle_group_incref(const char *name)
 void throttle_group_unref(ThrottleState *ts)
 {
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
-
-    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);
+    object_unref(OBJECT(tg));
 }
 
 /* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
@@ -572,9 +574,347 @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
     throttle_timers_detach_aio_context(tt);
 }
 
+#undef THROTTLE_OPT_PREFIX
+#define THROTTLE_OPT_PREFIX "x-"
+#define DOUBLE 0
+#define UINT64 1
+#define UNSIGNED 2
+
+typedef struct {
+    const char *name;
+    BucketType type;
+    int data_type;
+    const ptrdiff_t offset; /* offset in LeakyBucket struct. */
+} ThrottleParamInfo;
+
+static ThrottleParamInfo properties[] = {
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
+    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,
+    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
+    THROTTLE_OPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
+    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,
+    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, max),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
+    THROTTLE_OPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
+    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,
+    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
+    THROTTLE_OPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
+    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,
+    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
+    THROTTLE_BPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
+    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,
+    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, max),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
+    THROTTLE_BPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
+    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,
+    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
+    THROTTLE_BPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
+},
+{
+    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
+    0, UINT64, offsetof(ThrottleConfig, op_size),
+}
+};
+
+static void throttle_group_obj_init(Object *obj)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+
+    tg->clock_type = QEMU_CLOCK_REALTIME;
+    if (qtest_enabled()) {
+        /* For testing block IO throttling only */
+        tg->clock_type = QEMU_CLOCK_VIRTUAL;
+    }
+    tg->is_initialized = false;
+    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+    qemu_mutex_init(&tg->lock);
+    throttle_init(&tg->ts);
+    QLIST_INIT(&tg->head);
+}
+
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj), *iter;
+    ThrottleConfig *cfg = &tg->ts.cfg;
+
+    /* set group name to object id if it exists */
+    if (!tg->name && tg->parent_obj.parent) {
+        tg->name = object_get_canonical_path_component(OBJECT(obj));
+    }
+
+    if (tg->name) {
+        /* error if name is duplicate */
+        QTAILQ_FOREACH(iter, &throttle_groups, list) {
+            if (!g_strcmp0(tg->name, iter->name) && tg != iter) {
+                error_setg(errp, "A group with this name already exists");
+                return;
+            }
+        }
+    }
+
+    /* unfix buckets to check validity */
+    throttle_get_config(&tg->ts, cfg);
+    if (!throttle_is_valid(cfg, errp)) {
+        return;
+    }
+    /* fix buckets again */
+    throttle_config(&tg->ts, tg->clock_type, cfg);
+
+    tg->is_initialized = true;
+}
+
+static void throttle_group_obj_finalize(Object *obj)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    qemu_mutex_lock(&throttle_groups_lock);
+    QTAILQ_REMOVE(&throttle_groups, tg, list);
+    qemu_mutex_unlock(&throttle_groups_lock);
+    qemu_mutex_destroy(&tg->lock);
+    g_free(tg->name);
+}
+
+static void throttle_group_set(Object *obj, Visitor *v, const char * name,
+        void *opaque, Error **errp)
+
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    ThrottleConfig cfg;
+    ThrottleParamInfo *info = opaque;
+    ThrottleLimits *arg = NULL;
+    Error *local_err = NULL;
+    int64_t value;
+
+    /* If we have finished initialization, don't accept individual property
+     * changes through QOM. Throttle configuration limits must be set in one
+     * transaction, as certain combinations are invalid.
+     */
+    if (tg->is_initialized) {
+        error_setg(&local_err, "Property cannot be set after initialization");
+        goto fail;
+    }
+
+    visit_type_int64(v, name, &value, &local_err);
+    if (local_err) {
+        goto fail;
+    }
+    if (value < 0) {
+        error_setg(&local_err, "Property values cannot be negative");
+        goto fail;
+    }
+
+    cfg = tg->ts.cfg;
+    switch (info->data_type) {
+    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:
+        {
+            if (value > UINT_MAX) {
+                error_setg(&local_err, "%s value must be in the"
+                                       "range [0, %u]", info->name, UINT_MAX);
+                goto fail;
+            }
+            unsigned *field = (void *)&cfg.buckets[info->type] + info->offset;
+            *field = value;
+        }
+    }
+
+    tg->ts.cfg = cfg;
+    goto ret;
+
+fail:
+    error_propagate(errp, local_err);
+ret:
+    g_free(arg);
+    return;
+
+}
+
+static void throttle_group_get(Object *obj, Visitor *v, const char *name,
+        void *opaque, Error **errp)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    ThrottleConfig cfg;
+    ThrottleParamInfo *info = opaque;
+    int64_t value;
+
+    cfg = tg->ts.cfg;
+    switch (info->data_type) {
+    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_set_limits(Object *obj, Visitor *v,
+                                      const char *name, void *opaque,
+                                      Error **errp)
+
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    ThrottleConfig cfg;
+    ThrottleLimits *arg = NULL;
+    Error *local_err = NULL;
+
+    arg = g_new0(ThrottleLimits, 1);
+    visit_type_ThrottleLimits(v, name, &arg, &local_err);
+    if (local_err) {
+        goto fail;
+    }
+    qemu_mutex_lock(&tg->lock);
+    throttle_get_config(&tg->ts, &cfg);
+    throttle_limits_to_config(arg, &cfg, &local_err);
+    if (local_err) {
+        qemu_mutex_unlock(&tg->lock);
+        goto fail;
+    }
+    throttle_config(&tg->ts, tg->clock_type, &cfg);
+    qemu_mutex_unlock(&tg->lock);
+
+    goto ret;
+
+fail:
+    error_propagate(errp, local_err);
+ret:
+    g_free(arg);
+    return;
+}
+
+static void throttle_group_get_limits(Object *obj, Visitor *v,
+                                      const char *name, void *opaque,
+                                      Error **errp)
+{
+    ThrottleGroup *tg = THROTTLE_GROUP(obj);
+    ThrottleConfig cfg;
+    ThrottleLimits *arg = NULL;
+
+    arg = g_new0(ThrottleLimits, 1);
+    qemu_mutex_lock(&tg->lock);
+    throttle_get_config(&tg->ts, &cfg);
+    qemu_mutex_unlock(&tg->lock);
+    throttle_config_to_throttle_limits(&cfg, arg);
+    visit_type_ThrottleLimits(v, name, &arg, errp);
+    g_free(arg);
+}
+
+static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data)
+{
+    size_t i = 0;
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
+
+    ucc->complete = throttle_group_obj_complete;
+    /* individual properties */
+    for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) {
+        object_class_property_add(klass,
+                properties[i].name,
+                "int",
+                throttle_group_get,
+                throttle_group_set,
+                NULL, &properties[i],
+                &error_abort);
+    }
+
+    /* ThrottleLimits */
+    object_class_property_add(klass,
+                              "limits", "ThrottleLimits",
+                              throttle_group_get_limits,
+                              throttle_group_set_limits,
+                              NULL, NULL,
+                              &error_abort);
+}
+
+static const TypeInfo throttle_group_info = {
+   .name = TYPE_THROTTLE_GROUP,
+   .parent = TYPE_OBJECT,
+   .class_init = throttle_group_obj_class_init,
+   .instance_size = sizeof(ThrottleGroup),
+   .instance_init = throttle_group_obj_init,
+   .instance_finalize = throttle_group_obj_finalize,
+   .interfaces = (InterfaceInfo[]) {
+       { TYPE_USER_CREATABLE },
+       { }
+   },
+};
+
 static void throttle_groups_init(void)
 {
     qemu_mutex_init(&throttle_groups_lock);
+    type_register_static(&throttle_group_info);
 }
 
-block_init(throttle_groups_init);
+type_init(throttle_groups_init);
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index a0f27cac63..82f030523f 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -53,6 +53,9 @@ typedef struct ThrottleGroupMember {
 
 } ThrottleGroupMember;
 
+#define TYPE_THROTTLE_GROUP "throttle-group"
+#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), TYPE_THROTTLE_GROUP)
+
 const char *throttle_group_get_name(ThrottleGroupMember *tgm);
 
 ThrottleState *throttle_group_incref(const char *name);
diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
index 3133d1ca40..182b7896e1 100644
--- a/include/qemu/throttle-options.h
+++ b/include/qemu/throttle-options.h
@@ -10,81 +10,102 @@
 #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 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",\
         }
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index d056008c18..17e750b12d 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -152,5 +152,8 @@ bool throttle_schedule_timer(ThrottleState *ts,
                              bool is_write);
 
 void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
+void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
+                               Error **errp);
+void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits *var);
 
 #endif
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 833c602150..0bdc69aa5f 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1905,6 +1905,54 @@
             '*iops_size': 'int', '*group': 'str' } }
 
 ##
+# @ThrottleLimits:
+#
+# Limit parameters for throttling.
+# Since some limit combinations are illegal, limits should always be set in one
+# transaction. All fields are optional. When setting limits, if a field is
+# missing the current value is not changed.
+#
+# @iops-total:             limit total I/O operations per second
+# @iops-total-max:         I/O operations burst
+# @iops-total-max-length:  length of the iops-total-max burst period, in seconds
+#                          It must only be set if @iops-total-max is set as well.
+# @iops-read:              limit read operations per second
+# @iops-read-max:          I/O operations read burst
+# @iops-read-max-length:   length of the iops-read-max burst period, in seconds
+#                          It must only be set if @iops-read-max is set as well.
+# @iops-write:             limit write operations per second
+# @iops-write-max:         I/O operations write burst
+# @iops-write-max-length:  length of the iops-write-max burst period, in seconds
+#                          It must only be set if @iops-write-max is set as well.
+# @bps-total:              limit total bytes per second
+# @bps-total-max:          total bytes burst
+# @bps-total-max-length:   length of the bps-total-max burst period, in seconds.
+#                          It must only be set if @bps-total-max is set as well.
+# @bps-read:               limit read bytes per second
+# @bps-read-max:           total bytes read burst
+# @bps-read-max-length:    length of the bps-read-max burst period, in seconds
+#                          It must only be set if @bps-read-max is set as well.
+# @bps-write:              limit write bytes per second
+# @bps-write-max:          total bytes write burst
+# @bps-write-max-length:   length of the bps-write-max burst period, in seconds
+#                          It must only be set if @bps-write-max is set as well.
+# @iops-size:              when limiting by iops max size of an I/O in bytes
+#
+# Since: 2.11
+##
+{ 'struct': 'ThrottleLimits',
+  'data': { '*iops-total' : 'int', '*iops-total-max' : 'int',
+            '*iops-total-max-length' : 'int', '*iops-read' : 'int',
+            '*iops-read-max' : 'int', '*iops-read-max-length' : 'int',
+            '*iops-write' : 'int', '*iops-write-max' : 'int',
+            '*iops-write-max-length' : 'int', '*bps-total' : 'int',
+            '*bps-total-max' : 'int', '*bps-total-max-length' : 'int',
+            '*bps-read' : 'int', '*bps-read-max' : 'int',
+            '*bps-read-max-length' : 'int', '*bps-write' : 'int',
+            '*bps-write-max' : 'int', '*bps-write-max-length' : 'int',
+            '*iops-size' : 'int' } }
+
+##
 # @block-stream:
 #
 # Copy data from a backing file into a block device.
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 57cf5ba711..0ea9093eee 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -662,6 +662,7 @@ int main(int argc, char **argv)
     qemu_init_main_loop(&error_fatal);
     ctx = qemu_get_aio_context();
     bdrv_init();
+    module_call_init(MODULE_INIT_QOM);
 
     do {} while (g_main_context_iteration(NULL, false));
 
diff --git a/util/throttle.c b/util/throttle.c
index b2a52b8b34..99fd73157b 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -502,3 +502,154 @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
     }
 }
 
+/* return a ThrottleConfig based on the options in a ThrottleLimits
+ *
+ * @arg:    the ThrottleLimits object to read from
+ * @cfg:    the ThrottleConfig to edit
+ * @errp:   error object
+ */
+void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
+                               Error **errp)
+{
+    if (arg->has_bps_total) {
+        cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total;
+    }
+    if (arg->has_bps_read) {
+        cfg->buckets[THROTTLE_BPS_READ].avg  = arg->bps_read;
+    }
+    if (arg->has_bps_write) {
+        cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write;
+    }
+
+    if (arg->has_iops_total) {
+        cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total;
+    }
+    if (arg->has_iops_read) {
+        cfg->buckets[THROTTLE_OPS_READ].avg  = arg->iops_read;
+    }
+    if (arg->has_iops_write) {
+        cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write;
+    }
+
+    if (arg->has_bps_total_max) {
+        cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max;
+    }
+    if (arg->has_bps_read_max) {
+        cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max;
+    }
+    if (arg->has_bps_write_max) {
+        cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max;
+    }
+    if (arg->has_iops_total_max) {
+        cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max;
+    }
+    if (arg->has_iops_read_max) {
+        cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max;
+    }
+    if (arg->has_iops_write_max) {
+        cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max;
+    }
+
+    if (arg->has_bps_total_max_length) {
+        if (arg->bps_total_max_length > UINT_MAX) {
+            error_setg(errp, "bps-total-max-length value must be in"
+                    " the range [0, %u]", UINT_MAX);
+            return;
+        }
+        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length;
+    }
+    if (arg->has_bps_read_max_length) {
+        if (arg->bps_read_max_length > UINT_MAX) {
+            error_setg(errp, "bps-read-max-length value must be in"
+                    " the range [0, %u]", UINT_MAX);
+            return;
+        }
+        cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length;
+    }
+    if (arg->has_bps_write_max_length) {
+        if (arg->bps_write_max_length > UINT_MAX) {
+            error_setg(errp, "bps-write-max-length value must be in"
+                    " the range [0, %u]", UINT_MAX);
+            return;
+        }
+        cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length;
+    }
+    if (arg->has_iops_total_max_length) {
+        if (arg->iops_total_max_length > UINT_MAX) {
+            error_setg(errp, "iops-total-max-length value must be in"
+                    " the range [0, %u]", UINT_MAX);
+            return;
+        }
+        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length;
+    }
+    if (arg->has_iops_read_max_length) {
+        if (arg->iops_read_max_length > UINT_MAX) {
+            error_setg(errp, "iops-read-max-length value must be in"
+                    " the range [0, %u]", UINT_MAX);
+            return;
+        }
+        cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length;
+    }
+    if (arg->has_iops_write_max_length) {
+        if (arg->iops_write_max_length > UINT_MAX) {
+            error_setg(errp, "iops-write-max-length value must be in"
+                    " the range [0, %u]", UINT_MAX);
+            return;
+        }
+        cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length;
+    }
+
+    if (arg->has_iops_size) {
+        cfg->op_size = arg->iops_size;
+    }
+
+    throttle_is_valid(cfg, errp);
+}
+
+/* write the options of a ThrottleConfig to a ThrottleLimits
+ *
+ * @cfg:    the ThrottleConfig to read from
+ * @var:    the ThrottleLimits to write to
+ */
+void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits *var)
+{
+    var->bps_total               = cfg->buckets[THROTTLE_BPS_TOTAL].avg;
+    var->bps_read                = cfg->buckets[THROTTLE_BPS_READ].avg;
+    var->bps_write               = cfg->buckets[THROTTLE_BPS_WRITE].avg;
+    var->iops_total              = cfg->buckets[THROTTLE_OPS_TOTAL].avg;
+    var->iops_read               = cfg->buckets[THROTTLE_OPS_READ].avg;
+    var->iops_write              = cfg->buckets[THROTTLE_OPS_WRITE].avg;
+    var->bps_total_max           = cfg->buckets[THROTTLE_BPS_TOTAL].max;
+    var->bps_read_max            = cfg->buckets[THROTTLE_BPS_READ].max;
+    var->bps_write_max           = cfg->buckets[THROTTLE_BPS_WRITE].max;
+    var->iops_total_max          = cfg->buckets[THROTTLE_OPS_TOTAL].max;
+    var->iops_read_max           = cfg->buckets[THROTTLE_OPS_READ].max;
+    var->iops_write_max          = cfg->buckets[THROTTLE_OPS_WRITE].max;
+    var->bps_total_max_length    = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length;
+    var->bps_read_max_length     = cfg->buckets[THROTTLE_BPS_READ].burst_length;
+    var->bps_write_max_length    = cfg->buckets[THROTTLE_BPS_WRITE].burst_length;
+    var->iops_total_max_length   = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length;
+    var->iops_read_max_length    = cfg->buckets[THROTTLE_OPS_READ].burst_length;
+    var->iops_write_max_length   = cfg->buckets[THROTTLE_OPS_WRITE].burst_length;
+    var->iops_size               = cfg->op_size;
+
+    var->has_bps_total = true;
+    var->has_bps_read = true;
+    var->has_bps_write = true;
+    var->has_iops_total = true;
+    var->has_iops_read = true;
+    var->has_iops_write = true;
+    var->has_bps_total_max = true;
+    var->has_bps_read_max = true;
+    var->has_bps_write_max = true;
+    var->has_iops_total_max = true;
+    var->has_iops_read_max = true;
+    var->has_iops_write_max = true;
+    var->has_bps_read_max_length = true;
+    var->has_bps_total_max_length = true;
+    var->has_bps_write_max_length = true;
+    var->has_iops_total_max_length = true;
+    var->has_iops_read_max_length = true;
+    var->has_iops_write_max_length = true;
+    var->has_iops_size = true;
+}
-- 
2.11.0

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

* [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
                   ` (3 preceding siblings ...)
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
@ 2017-07-31  9:54 ` Manos Pitsidianakis
  2017-08-01 16:14   ` Stefan Hajnoczi
                     ` (2 more replies)
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 6/7] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests Manos Pitsidianakis
  6 siblings, 3 replies; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

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 syntax
-drive driver=throttle,file.filename=foo.qcow2, \
        limits.iops-total=...,throttle-group=bar

The configuration flags and their semantics are identical to the
hardcoded throttling ones.

A node can be created referring to an existing group, and will overwrite
its limits if any are specified, otherwise they are retained.

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

diff --git a/block/Makefile.objs b/block/Makefile.objs
index 2aaede4ae1..6eaf78a046 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..f3395462fb
--- /dev/null
+++ b/block/throttle.c
@@ -0,0 +1,395 @@
+/*
+ * 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"
+
+#undef THROTTLE_OPT_PREFIX
+#define THROTTLE_OPT_PREFIX "limits."
+static QemuOptsList throttle_opts = {
+    .name = "throttle",
+    .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
+    .desc = {
+        THROTTLE_OPTS,
+        {
+            .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 int throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg,
+                                     Error **errp)
+{
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL)) {
+        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
+                                0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ)) {
+        cfg->buckets[THROTTLE_BPS_READ].avg  =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
+                                0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE)) {
+        cfg->buckets[THROTTLE_BPS_WRITE].avg =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
+                                0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL)) {
+        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
+                                0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ)) {
+        cfg->buckets[THROTTLE_OPS_READ].avg =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
+                                0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE)) {
+        cfg->buckets[THROTTLE_OPS_WRITE].avg =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
+                                0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX)) {
+        cfg->buckets[THROTTLE_BPS_TOTAL].max =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_TOTAL_MAX, 0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX)) {
+        cfg->buckets[THROTTLE_BPS_READ].max  =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_READ_MAX, 0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX)) {
+        cfg->buckets[THROTTLE_BPS_WRITE].max =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_WRITE_MAX, 0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX)) {
+        cfg->buckets[THROTTLE_OPS_TOTAL].max =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_TOTAL_MAX, 0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX)) {
+        cfg->buckets[THROTTLE_OPS_READ].max =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_READ_MAX, 0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX)) {
+        cfg->buckets[THROTTLE_OPS_WRITE].max =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_WRITE_MAX, 0);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
+        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
+            error_setg(errp, "%s value must be in the range [0, %u]",
+                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
+                       UINT_MAX);
+            return -1;
+        }
+        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH)) {
+        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
+            error_setg(errp, "%s must be in the range [0, %u]",
+                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
+                       UINT_MAX);
+            return -1;
+        }
+        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
+        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
+            error_setg(errp, "%s must be in the range [0, %u]",
+                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
+                       UINT_MAX);
+            return -1;
+        }
+        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
+        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
+            error_setg(errp, "%s must be in the range [0, %u]",
+                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
+                       UINT_MAX);
+            return -1;
+        }
+        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
+        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
+            error_setg(errp, "%s must be in the range [0, %u]",
+                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
+                       UINT_MAX);
+            return -1;
+        }
+        cfg->buckets[THROTTLE_OPS_READ].burst_length =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
+        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
+            error_setg(errp, "%s must be in the range [0, %u]",
+                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
+                       UINT_MAX);
+            return -1;
+        }
+        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
+                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
+    }
+    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE)) {
+        cfg->op_size =
+            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
+                                0);
+    }
+    return 0;
+}
+
+static int throttle_configure_tgm(BlockDriverState *bs,
+                                  ThrottleGroupMember *tgm,
+                                  QDict *options, Error **errp)
+{
+    int ret;
+    ThrottleConfig cfg;
+    const char *group_name = NULL;
+    Error *local_err = NULL;
+    QemuOpts *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) {
+        error_propagate(errp, local_err);
+        goto err;
+    }
+
+    /* If no name is specified, an anonymous group will be created */
+    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
+
+    /* Register membership to group with name group_name */
+    throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
+
+    /* Copy previous configuration */
+    throttle_group_get_config(tgm, &cfg);
+
+    /* Change limits if user has specified them */
+    if (throttle_extract_options(opts, &cfg, errp) ||
+        !throttle_is_valid(&cfg, errp)) {
+        throttle_group_unregister_tgm(tgm);
+        goto err;
+    }
+    /* Update group configuration */
+    throttle_group_config(tgm, &cfg);
+
+    ret = 0;
+    goto fin;
+
+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;
+
+    bs->file = bdrv_open_child(NULL, options, "file",
+                                    bs, &child_file, false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
+    return throttle_configure_tgm(bs, tgm, options, errp);
+}
+
+static void throttle_close(BlockDriverState *bs)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_group_unregister_tgm(tgm);
+}
+
+
+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_group_detach_aio_context(tgm);
+}
+
+static void throttle_attach_aio_context(BlockDriverState *bs,
+                                    AioContext *new_context)
+{
+    ThrottleGroupMember *tgm = bs->opaque;
+    throttle_group_attach_aio_context(tgm, 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;
+
+    throttle_group_unregister_tgm(tgm);
+    g_free(state->bs->opaque);
+    state->bs->opaque = state->opaque;
+    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 bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
+                                                 BlockDriverState *candidate)
+{
+    return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
+}
+
+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   =   throttle_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 182b7896e1..3528a8f4a2 100644
--- a/include/qemu/throttle-options.h
+++ b/include/qemu/throttle-options.h
@@ -29,6 +29,7 @@
 #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 "throttle-group"
 
 #define THROTTLE_OPT_PREFIX "throttling."
 #define THROTTLE_OPTS \
-- 
2.11.0

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

* [Qemu-devel] [PATCH v3 6/7] block: add BlockDevOptionsThrottle to QAPI
  2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
                   ` (4 preceding siblings ...)
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver Manos Pitsidianakis
@ 2017-07-31  9:54 ` Manos Pitsidianakis
  2017-08-01 16:16   ` Stefan Hajnoczi
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests Manos Pitsidianakis
  6 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

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

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

diff --git a/qapi/block-core.json b/qapi/block-core.json
index 0bdc69aa5f..f5ce67c4fb 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2222,6 +2222,7 @@
 # Drivers that are supported in block device operations.
 #
 # @vxhs: Since 2.10
+# @throttle: Since 2.11
 #
 # Since: 2.9
 ##
@@ -2231,7 +2232,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:
@@ -3095,6 +3096,22 @@
             '*tls-creds': 'str' } }
 
 ##
+# @BlockdevOptionsThrottle:
+#
+# Driver specific block device options for the throttle driver
+#
+# @throttle-group:   the name of the throttle-group object to use. It will be
+#                    created if it doesn't already exist
+# @file:             reference to or definition of the data source block device
+# @limits:           ThrottleLimits options
+# Since: 2.11
+##
+{ 'struct': 'BlockdevOptionsThrottle',
+  'data': { '*throttle-group': 'str',
+            'file' : 'BlockdevRef',
+            '*limits' : 'ThrottleLimits'
+             } }
+##
 # @BlockdevOptions:
 #
 # Options for creating a block device.  Many options are available for all
@@ -3155,6 +3172,7 @@
       'replication':'BlockdevOptionsReplication',
       'sheepdog':   'BlockdevOptionsSheepdog',
       'ssh':        'BlockdevOptionsSsh',
+      'throttle':   'BlockdevOptionsThrottle',
       'vdi':        'BlockdevOptionsGenericFormat',
       'vhdx':       'BlockdevOptionsGenericFormat',
       'vmdk':       'BlockdevOptionsGenericCOWFormat',
-- 
2.11.0

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

* [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
  2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
                   ` (5 preceding siblings ...)
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 6/7] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
@ 2017-07-31  9:54 ` Manos Pitsidianakis
  2017-08-03  8:07   ` Kevin Wolf
  6 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-07-31  9:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, Alberto Garcia, qemu-block

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
---
 tests/qemu-iotests/184     | 237 +++++++++++++++++++++++++++++++++
 tests/qemu-iotests/184.out | 319 +++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/group   |   1 +
 3 files changed, 557 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..c3f3f2b7ca
--- /dev/null
+++ b/tests/qemu-iotests/184
@@ -0,0 +1,237 @@
+#!/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="Manos Pitsidianakis"
+
+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
+_supported_proto file
+_supported_os Linux
+
+function do_run_qemu()
+{
+    echo Testing: "$@" | _filter_imgfmt
+    $QEMU -nographic -qmp-pretty 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
+
+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": "throttle-group",
+    "id": "group0",
+    "props": {
+      "limits" : {
+        "iops-total": 1000
+      }
+    }
+  }
+}
+{ "execute": "blockdev-add",
+  "arguments": {
+    "driver": "throttle",
+    "node-name": "throttle0",
+    "throttle-group": "group0",
+    "file": "disk0"
+  }
+}
+{ "execute": "query-named-block-nodes" }
+{ "execute": "query-block" }
+{ "execute": "quit" }
+EOF
+
+echo
+echo "== create anonymous group =="
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+  "arguments": {
+    "driver": "$IMGFMT",
+    "node-name": "disk0",
+    "file": {
+      "driver": "file",
+      "filename": "$TEST_IMG"
+    }
+  }
+}
+{ "execute": "blockdev-add",
+  "arguments": {
+    "driver": "throttle",
+    "node-name": "throttle-anonymous",
+    "limits" : {
+      "iops-total" : 1000
+    },
+    "file": "disk0"
+  }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "== property changes in ThrottleGroup =="
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "object-add",
+  "arguments": {
+    "qom-type": "throttle-group",
+    "id": "group0",
+    "props" : {
+      "limits": {
+          "iops-total": 1000
+      }
+    }
+  }
+}
+{ "execute" : "qom-get",
+  "arguments" : {
+    "path" : "group0",
+    "property" : "limits"
+  }
+}
+{ "execute" : "qom-set",
+    "arguments" : {
+        "path" : "group0",
+        "property" : "limits",
+        "value" : {
+            "iops-total" : 0
+        }
+    }
+}
+{ "execute" : "qom-get",
+  "arguments" : {
+    "path" : "group0",
+    "property" : "limits"
+  }
+}
+{ "execute": "blockdev-add",
+  "arguments": {
+    "driver": "$IMGFMT",
+    "node-name": "disk0",
+    "file": {
+      "driver": "file",
+      "filename": "$TEST_IMG"
+    }
+  }
+}
+{ "execute": "blockdev-add",
+  "arguments": {
+    "driver": "throttle",
+    "node-name": "throttle0",
+    "throttle-group": "group0",
+    "limits" : {
+      "iops-total" : 128,
+      "iops-total-max" : 256,
+      "iops-total-max-length" : 60
+    },
+    "file": "disk0"
+  }
+}
+{ "execute" : "qom-get",
+  "arguments" : {
+    "path" : "group0",
+    "property" : "limits"
+  }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "== object creation/set errors  =="
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "object-add",
+  "arguments": {
+    "qom-type": "throttle-group",
+    "id": "group0",
+    "props" : {
+      "limits": {
+          "iops-total": 1000
+      }
+    }
+  }
+}
+{ "execute" : "qom-set",
+  "arguments" : {
+    "path" : "group0",
+    "property" : "x-iops-total",
+    "value" : 0
+  }
+}
+{ "execute" : "qom-set",
+    "arguments" : {
+        "path" : "group0",
+        "property" : "limits",
+        "value" : {
+            "iops-total" : 10,
+            "iops-read" : 10
+        }
+    }
+}
+{ "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..1b9ac3ea65
--- /dev/null
+++ b/tests/qemu-iotests/184.out
@@ -0,0 +1,319 @@
+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:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"raw\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.raw\"}}}",
+                "format": "throttle",
+                "actual-size": 0
+            },
+            "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:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"raw\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.raw\"}}}",
+            "encryption_key_missing": false
+        },
+        {
+            "iops_rd": 0,
+            "detect_zeroes": "off",
+            "image": {
+                "virtual-size": 67108864,
+                "filename": "TEST_DIR/t.raw",
+                "format": "raw",
+                "actual-size": 0,
+                "dirty-flag": false
+            },
+            "iops_wr": 0,
+            "ro": false,
+            "node-name": "disk0",
+            "backing_file_depth": 0,
+            "drv": "raw",
+            "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.raw",
+            "encryption_key_missing": false
+        },
+        {
+            "iops_rd": 0,
+            "detect_zeroes": "off",
+            "image": {
+                "virtual-size": 67108864,
+                "filename": "TEST_DIR/t.raw",
+                "format": "file",
+                "actual-size": 0,
+                "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.raw",
+            "encryption_key_missing": false
+        }
+    ]
+}
+{
+    "return": [
+    ]
+}
+{
+    "return": {
+    }
+}
+{
+    "timestamp": {
+        "seconds":  TIMESTAMP,
+        "microseconds":  TIMESTAMP
+    },
+    "event": "SHUTDOWN",
+    "data": {
+        "guest": false
+    }
+}
+
+
+== create anonymous group ==
+Testing:
+{
+    QMP_VERSION
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "timestamp": {
+        "seconds":  TIMESTAMP,
+        "microseconds":  TIMESTAMP
+    },
+    "event": "SHUTDOWN",
+    "data": {
+        "guest": false
+    }
+}
+
+
+== property changes in ThrottleGroup ==
+Testing:
+{
+    QMP_VERSION
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+        "bps-read-max-length": 1,
+        "iops-read-max-length": 1,
+        "bps-read-max": 0,
+        "bps-total": 0,
+        "iops-total-max-length": 1,
+        "iops-total": 1000,
+        "iops-write-max": 0,
+        "bps-write": 0,
+        "bps-total-max": 0,
+        "bps-write-max": 0,
+        "iops-size": 0,
+        "iops-read": 0,
+        "iops-write-max-length": 1,
+        "iops-write": 0,
+        "bps-total-max-length": 1,
+        "iops-read-max": 0,
+        "bps-read": 0,
+        "bps-write-max-length": 1,
+        "iops-total-max": 0
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+        "bps-read-max-length": 1,
+        "iops-read-max-length": 1,
+        "bps-read-max": 0,
+        "bps-total": 0,
+        "iops-total-max-length": 1,
+        "iops-total": 0,
+        "iops-write-max": 0,
+        "bps-write": 0,
+        "bps-total-max": 0,
+        "bps-write-max": 0,
+        "iops-size": 0,
+        "iops-read": 0,
+        "iops-write-max-length": 1,
+        "iops-write": 0,
+        "bps-total-max-length": 1,
+        "iops-read-max": 0,
+        "bps-read": 0,
+        "bps-write-max-length": 1,
+        "iops-total-max": 0
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+        "bps-read-max-length": 1,
+        "iops-read-max-length": 1,
+        "bps-read-max": 0,
+        "bps-total": 0,
+        "iops-total-max-length": 60,
+        "iops-total": 128,
+        "iops-write-max": 0,
+        "bps-write": 0,
+        "bps-total-max": 0,
+        "bps-write-max": 0,
+        "iops-size": 0,
+        "iops-read": 0,
+        "iops-write-max-length": 1,
+        "iops-write": 0,
+        "bps-total-max-length": 1,
+        "iops-read-max": 0,
+        "bps-read": 0,
+        "bps-write-max-length": 1,
+        "iops-total-max": 256
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "timestamp": {
+        "seconds":  TIMESTAMP,
+        "microseconds":  TIMESTAMP
+    },
+    "event": "SHUTDOWN",
+    "data": {
+        "guest": false
+    }
+}
+
+
+== object creation/set errors  ==
+Testing:
+{
+    QMP_VERSION
+}
+{
+    "return": {
+    }
+}
+{
+    "return": {
+    }
+}
+{
+    "error": {
+        "class": "GenericError",
+        "desc": "Property cannot be set after initialization"
+    }
+}
+{
+    "error": {
+        "class": "GenericError",
+        "desc": "bps/iops/max total values and read/write values cannot be used at the same time"
+    }
+}
+{
+    "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 287f0ea27d..76483ea029 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -180,6 +180,7 @@
 181 rw auto migration
 182 rw auto quick
 183 rw auto migration
+184 rw auto quick
 185 rw auto
 186 rw auto
 188 rw auto quick
-- 
2.11.0

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
@ 2017-08-01 15:47   ` Stefan Hajnoczi
  2017-08-01 16:49     ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Hajnoczi @ 2017-08-01 15:47 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Alberto Garcia, qemu-block

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

On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
> ThrottleGroup is converted to an object. This will allow the future
> throttle block filter drive easy creation and configuration of throttle
> groups in QMP and cli.
> 
> A new QAPI struct, ThrottleLimits, is introduced to provide a shared
> struct for all throttle configuration needs in QMP.
> 
> ThrottleGroups can be created via CLI as
>     -object throttle-group,id=foo,x-iops-total=100,x-..
> where x-* are individual limit properties. Since we can't add non-scalar
> properties in -object this interface must be used instead. However,
> setting these properties must be disabled after initialization because
> certain combinations of limits are forbidden and thus configuration
> changes should be done in one transaction. The individual properties
> will go away when support for non-scalar values in CLI is implemented
> and thus are marked as experimental.
> 
> ThrottleGroup also has a `limits` property that uses the ThrottleLimits
> struct.  It can be used to create ThrottleGroups or set the
> configuration in existing groups as follows:
> 
> { "execute": "object-add",
>   "arguments": {
>     "qom-type": "throttle-group",
>     "id": "foo",
>     "props" : {
>       "limits": {
>           "iops-total": 100
>       }
>     }
>   }
> }
> { "execute" : "qom-set",
>     "arguments" : {
>         "path" : "foo",
>         "property" : "limits",
>         "value" : {
>             "iops-total" : 99
>         }
>     }
> }
> 
> This also means a group's configuration can be fetched with qom-get.
> 
> ThrottleGroups can be anonymous which means they can't get accessed by
> other users ie they will always be units instead of group (Because they
> have one ThrottleGroupMember).

blockdev.c automatically assigns -drive id= to the group name if
throttling.group= wasn't given.  So who will use anonymous single-drive
mode?

> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
> Notes:
>     Note: I tested Markus Armbruster's patch in <87wp7fghi9.fsf@dusky.pond.sub.org>
>     on master and I can use this syntax successfuly:
>     -object '{ "qom-type" : "throttle-group", "id" : "foo", "props" : { "limits" \
>     : { "iops-total" : 1000 } } }'
>     If this gets merged using -object will be a little more verbose but at least we
>     won't have seperate properties, which is a good thing, so the x-* should be
>     dropped.
> 
>  block/throttle-groups.c         | 402 ++++++++++++++++++++++++++++++++++++----
>  include/block/throttle-groups.h |   3 +
>  include/qemu/throttle-options.h |  59 ++++--
>  include/qemu/throttle.h         |   3 +
>  qapi/block-core.json            |  48 +++++
>  tests/test-throttle.c           |   1 +
>  util/throttle.c                 | 151 +++++++++++++++
>  7 files changed, 617 insertions(+), 50 deletions(-)
> 
> diff --git a/block/throttle-groups.c b/block/throttle-groups.c
> index f711a3dc62..b9c5036e44 100644
> --- a/block/throttle-groups.c
> +++ b/block/throttle-groups.c
> @@ -25,9 +25,17 @@
>  #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"
> +#include "qapi-visit.h"
> +#include "qom/object.h"
> +#include "qom/object_interfaces.h"
> +
> +static void throttle_group_obj_init(Object *obj);
> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
>  
>  /* The ThrottleGroup structure (with its ThrottleState) is shared
>   * among different ThrottleGroupMembers and it's independent from
> @@ -54,6 +62,10 @@
>   * that ThrottleGroupMember has throttled requests in the queue.
>   */
>  typedef struct ThrottleGroup {
> +    Object parent_obj;
> +
> +    /* refuse individual property change if initialization is complete */
> +    bool is_initialized;
>      char *name; /* This is constant during the lifetime of the group */
>  
>      QemuMutex lock; /* This lock protects the following four fields */
> @@ -63,8 +75,7 @@ typedef struct ThrottleGroup {
>      bool any_timer_armed[2];
>      QEMUClockType clock_type;
>  
> -    /* These two are protected by the global throttle_groups_lock */
> -    unsigned refcount;
> +    /* This is protected by the global throttle_groups_lock */
>      QTAILQ_ENTRY(ThrottleGroup) list;
>  } ThrottleGroup;
>  
> @@ -77,7 +88,8 @@ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
>   * If no ThrottleGroup is found with the given name a new one is
>   * created.
>   *
> - * @name: the name of the ThrottleGroup
> + * @name: the name of the ThrottleGroup, NULL means a new anonymous group will
> + *        be created.
>   * @ret:  the ThrottleState member of the ThrottleGroup
>   */
>  ThrottleState *throttle_group_incref(const char *name)
> @@ -87,32 +99,30 @@ ThrottleState *throttle_group_incref(const char *name)
>  
>      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)) {
> -            tg = iter;
> -            break;
> +    if (name) {
> +        /* Look for an existing group with that name */
> +        QTAILQ_FOREACH(iter, &throttle_groups, list) {
> +            if (!g_strcmp0(name, iter->name)) {
> +                tg = iter;
> +                break;
> +            }
>          }
>      }
>  
>      /* Create a new one if not found */
>      if (!tg) {
> -        tg = g_new0(ThrottleGroup, 1);
> +        /* new ThrottleGroup obj will have a refcnt = 1 */
> +        tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
>          tg->name = g_strdup(name);
> -        tg->clock_type = QEMU_CLOCK_REALTIME;
> -
> -        if (qtest_enabled()) {
> -            /* For testing block IO throttling only */
> -            tg->clock_type = QEMU_CLOCK_VIRTUAL;
> -        }
> -        qemu_mutex_init(&tg->lock);
> -        throttle_init(&tg->ts);
> -        QLIST_INIT(&tg->head);
> -
> -        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> +        throttle_group_obj_complete((UserCreatable *)tg, &error_abort);
>      }
>  
> -    tg->refcount++;
> +    qemu_mutex_lock(&tg->lock);
> +    if (!QLIST_EMPTY(&tg->head)) {
> +        /* only ref if the group is not empty */
> +        object_ref(OBJECT(tg));
> +    }
> +    qemu_mutex_unlock(&tg->lock);

How do the refcounts work in various cases (legacy -drive
throttling.group and -object throttle-group with 0, 1, or multiple
drives)?

It's not obvious to me that this code works in all cases.

>  
>      qemu_mutex_unlock(&throttle_groups_lock);
>  
> @@ -129,15 +139,7 @@ ThrottleState *throttle_group_incref(const char *name)
>  void throttle_group_unref(ThrottleState *ts)
>  {
>      ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
> -
> -    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);
> +    object_unref(OBJECT(tg));
>  }
>  
>  /* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
> @@ -572,9 +574,347 @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
>      throttle_timers_detach_aio_context(tt);
>  }
>  
> +#undef THROTTLE_OPT_PREFIX
> +#define THROTTLE_OPT_PREFIX "x-"
> +#define DOUBLE 0
> +#define UINT64 1
> +#define UNSIGNED 2
> +
> +typedef struct {
> +    const char *name;
> +    BucketType type;
> +    int data_type;
> +    const ptrdiff_t offset; /* offset in LeakyBucket struct. */
> +} ThrottleParamInfo;
> +
> +static ThrottleParamInfo properties[] = {
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
> +    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,
> +    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
> +    THROTTLE_OPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
> +    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,
> +    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
> +    THROTTLE_OPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
> +    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,
> +    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
> +    THROTTLE_OPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
> +    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,
> +    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
> +    THROTTLE_BPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
> +    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,
> +    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
> +    THROTTLE_BPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
> +    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,
> +    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
> +    THROTTLE_BPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
> +    0, UINT64, offsetof(ThrottleConfig, op_size),
> +}
> +};
> +
> +static void throttle_group_obj_init(Object *obj)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +
> +    tg->clock_type = QEMU_CLOCK_REALTIME;
> +    if (qtest_enabled()) {
> +        /* For testing block IO throttling only */
> +        tg->clock_type = QEMU_CLOCK_VIRTUAL;
> +    }
> +    tg->is_initialized = false;
> +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);

Is the lock taken when the object-add QMP command or -object
command-line argument are used?

In any case, please do not assume that throttle_groups_lock is held for
Object->init().  It should be possible to create new instances at any
time.

> +    qemu_mutex_init(&tg->lock);
> +    throttle_init(&tg->ts);
> +    QLIST_INIT(&tg->head);
> +}
> +
> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj), *iter;
> +    ThrottleConfig *cfg = &tg->ts.cfg;
> +
> +    /* set group name to object id if it exists */
> +    if (!tg->name && tg->parent_obj.parent) {
> +        tg->name = object_get_canonical_path_component(OBJECT(obj));
> +    }
> +
> +    if (tg->name) {
> +        /* error if name is duplicate */
> +        QTAILQ_FOREACH(iter, &throttle_groups, list) {

Same locking issue: is throttle_groups_lock held here in all cases?  I
think it's not held in the object-add QMP and -object throttle-group
cases.

> +            if (!g_strcmp0(tg->name, iter->name) && tg != iter) {
> +                error_setg(errp, "A group with this name already exists");
> +                return;
> +            }
> +        }
> +    }
> +
> +    /* unfix buckets to check validity */
> +    throttle_get_config(&tg->ts, cfg);
> +    if (!throttle_is_valid(cfg, errp)) {
> +        return;
> +    }
> +    /* fix buckets again */
> +    throttle_config(&tg->ts, tg->clock_type, cfg);
> +
> +    tg->is_initialized = true;
> +}
> +
> +static void throttle_group_obj_finalize(Object *obj)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    qemu_mutex_lock(&throttle_groups_lock);
> +    QTAILQ_REMOVE(&throttle_groups, tg, list);
> +    qemu_mutex_unlock(&throttle_groups_lock);
> +    qemu_mutex_destroy(&tg->lock);
> +    g_free(tg->name);
> +}
> +
> +static void throttle_group_set(Object *obj, Visitor *v, const char * name,
> +        void *opaque, Error **errp)
> +
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleParamInfo *info = opaque;
> +    ThrottleLimits *arg = NULL;
> +    Error *local_err = NULL;
> +    int64_t value;
> +
> +    /* If we have finished initialization, don't accept individual property
> +     * changes through QOM. Throttle configuration limits must be set in one
> +     * transaction, as certain combinations are invalid.
> +     */
> +    if (tg->is_initialized) {
> +        error_setg(&local_err, "Property cannot be set after initialization");
> +        goto fail;
> +    }
> +
> +    visit_type_int64(v, name, &value, &local_err);
> +    if (local_err) {
> +        goto fail;
> +    }
> +    if (value < 0) {
> +        error_setg(&local_err, "Property values cannot be negative");
> +        goto fail;
> +    }
> +
> +    cfg = tg->ts.cfg;
> +    switch (info->data_type) {
> +    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:
> +        {
> +            if (value > UINT_MAX) {
> +                error_setg(&local_err, "%s value must be in the"
> +                                       "range [0, %u]", info->name, UINT_MAX);
> +                goto fail;
> +            }
> +            unsigned *field = (void *)&cfg.buckets[info->type] + info->offset;
> +            *field = value;
> +        }
> +    }
> +
> +    tg->ts.cfg = cfg;
> +    goto ret;
> +
> +fail:
> +    error_propagate(errp, local_err);
> +ret:
> +    g_free(arg);
> +    return;

arg is unused, please drop it.

> +
> +}
> +
> +static void throttle_group_get(Object *obj, Visitor *v, const char *name,
> +        void *opaque, Error **errp)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleParamInfo *info = opaque;
> +    int64_t value;
> +
> +    cfg = tg->ts.cfg;
> +    switch (info->data_type) {
> +    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_set_limits(Object *obj, Visitor *v,
> +                                      const char *name, void *opaque,
> +                                      Error **errp)
> +
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleLimits *arg = NULL;
> +    Error *local_err = NULL;
> +
> +    arg = g_new0(ThrottleLimits, 1);

Why does ThrottleLimits need to be allocated on the heap?

> +    visit_type_ThrottleLimits(v, name, &arg, &local_err);
> +    if (local_err) {
> +        goto fail;
> +    }
> +    qemu_mutex_lock(&tg->lock);
> +    throttle_get_config(&tg->ts, &cfg);
> +    throttle_limits_to_config(arg, &cfg, &local_err);
> +    if (local_err) {
> +        qemu_mutex_unlock(&tg->lock);
> +        goto fail;
> +    }
> +    throttle_config(&tg->ts, tg->clock_type, &cfg);
> +    qemu_mutex_unlock(&tg->lock);
> +
> +    goto ret;

error_propagate() is a nop if local_err is NULL so this goto is
unnecessary.

> +
> +fail:
> +    error_propagate(errp, local_err);
> +ret:
> +    g_free(arg);
> +    return;
> +}
> +
> +static void throttle_group_get_limits(Object *obj, Visitor *v,
> +                                      const char *name, void *opaque,
> +                                      Error **errp)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleLimits *arg = NULL;
> +
> +    arg = g_new0(ThrottleLimits, 1);
> +    qemu_mutex_lock(&tg->lock);
> +    throttle_get_config(&tg->ts, &cfg);
> +    qemu_mutex_unlock(&tg->lock);
> +    throttle_config_to_throttle_limits(&cfg, arg);
> +    visit_type_ThrottleLimits(v, name, &arg, errp);
> +    g_free(arg);
> +}
> +
> +static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data)
> +{
> +    size_t i = 0;
> +    UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
> +
> +    ucc->complete = throttle_group_obj_complete;
> +    /* individual properties */
> +    for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) {
> +        object_class_property_add(klass,
> +                properties[i].name,
> +                "int",
> +                throttle_group_get,
> +                throttle_group_set,
> +                NULL, &properties[i],
> +                &error_abort);
> +    }
> +
> +    /* ThrottleLimits */
> +    object_class_property_add(klass,
> +                              "limits", "ThrottleLimits",
> +                              throttle_group_get_limits,
> +                              throttle_group_set_limits,
> +                              NULL, NULL,
> +                              &error_abort);
> +}
> +
> +static const TypeInfo throttle_group_info = {
> +   .name = TYPE_THROTTLE_GROUP,
> +   .parent = TYPE_OBJECT,
> +   .class_init = throttle_group_obj_class_init,
> +   .instance_size = sizeof(ThrottleGroup),
> +   .instance_init = throttle_group_obj_init,
> +   .instance_finalize = throttle_group_obj_finalize,
> +   .interfaces = (InterfaceInfo[]) {
> +       { TYPE_USER_CREATABLE },
> +       { }
> +   },
> +};
> +
>  static void throttle_groups_init(void)
>  {
>      qemu_mutex_init(&throttle_groups_lock);
> +    type_register_static(&throttle_group_info);
>  }
>  
> -block_init(throttle_groups_init);
> +type_init(throttle_groups_init);
> diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
> index a0f27cac63..82f030523f 100644
> --- a/include/block/throttle-groups.h
> +++ b/include/block/throttle-groups.h
> @@ -53,6 +53,9 @@ typedef struct ThrottleGroupMember {
>  
>  } ThrottleGroupMember;
>  
> +#define TYPE_THROTTLE_GROUP "throttle-group"
> +#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), TYPE_THROTTLE_GROUP)
> +
>  const char *throttle_group_get_name(ThrottleGroupMember *tgm);
>  
>  ThrottleState *throttle_group_incref(const char *name);
> diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
> index 3133d1ca40..182b7896e1 100644
> --- a/include/qemu/throttle-options.h
> +++ b/include/qemu/throttle-options.h
> @@ -10,81 +10,102 @@
>  #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 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",\
>          }
> diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
> index d056008c18..17e750b12d 100644
> --- a/include/qemu/throttle.h
> +++ b/include/qemu/throttle.h
> @@ -152,5 +152,8 @@ bool throttle_schedule_timer(ThrottleState *ts,
>                               bool is_write);
>  
>  void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
> +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
> +                               Error **errp);
> +void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits *var);
>  
>  #endif
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 833c602150..0bdc69aa5f 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -1905,6 +1905,54 @@
>              '*iops_size': 'int', '*group': 'str' } }
>  
>  ##
> +# @ThrottleLimits:
> +#
> +# Limit parameters for throttling.
> +# Since some limit combinations are illegal, limits should always be set in one
> +# transaction. All fields are optional. When setting limits, if a field is
> +# missing the current value is not changed.
> +#
> +# @iops-total:             limit total I/O operations per second
> +# @iops-total-max:         I/O operations burst
> +# @iops-total-max-length:  length of the iops-total-max burst period, in seconds
> +#                          It must only be set if @iops-total-max is set as well.
> +# @iops-read:              limit read operations per second
> +# @iops-read-max:          I/O operations read burst
> +# @iops-read-max-length:   length of the iops-read-max burst period, in seconds
> +#                          It must only be set if @iops-read-max is set as well.
> +# @iops-write:             limit write operations per second
> +# @iops-write-max:         I/O operations write burst
> +# @iops-write-max-length:  length of the iops-write-max burst period, in seconds
> +#                          It must only be set if @iops-write-max is set as well.
> +# @bps-total:              limit total bytes per second
> +# @bps-total-max:          total bytes burst
> +# @bps-total-max-length:   length of the bps-total-max burst period, in seconds.
> +#                          It must only be set if @bps-total-max is set as well.
> +# @bps-read:               limit read bytes per second
> +# @bps-read-max:           total bytes read burst
> +# @bps-read-max-length:    length of the bps-read-max burst period, in seconds
> +#                          It must only be set if @bps-read-max is set as well.
> +# @bps-write:              limit write bytes per second
> +# @bps-write-max:          total bytes write burst
> +# @bps-write-max-length:   length of the bps-write-max burst period, in seconds
> +#                          It must only be set if @bps-write-max is set as well.
> +# @iops-size:              when limiting by iops max size of an I/O in bytes
> +#
> +# Since: 2.11
> +##
> +{ 'struct': 'ThrottleLimits',
> +  'data': { '*iops-total' : 'int', '*iops-total-max' : 'int',
> +            '*iops-total-max-length' : 'int', '*iops-read' : 'int',
> +            '*iops-read-max' : 'int', '*iops-read-max-length' : 'int',
> +            '*iops-write' : 'int', '*iops-write-max' : 'int',
> +            '*iops-write-max-length' : 'int', '*bps-total' : 'int',
> +            '*bps-total-max' : 'int', '*bps-total-max-length' : 'int',
> +            '*bps-read' : 'int', '*bps-read-max' : 'int',
> +            '*bps-read-max-length' : 'int', '*bps-write' : 'int',
> +            '*bps-write-max' : 'int', '*bps-write-max-length' : 'int',
> +            '*iops-size' : 'int' } }
> +
> +##
>  # @block-stream:
>  #
>  # Copy data from a backing file into a block device.
> diff --git a/tests/test-throttle.c b/tests/test-throttle.c
> index 57cf5ba711..0ea9093eee 100644
> --- a/tests/test-throttle.c
> +++ b/tests/test-throttle.c
> @@ -662,6 +662,7 @@ int main(int argc, char **argv)
>      qemu_init_main_loop(&error_fatal);
>      ctx = qemu_get_aio_context();
>      bdrv_init();
> +    module_call_init(MODULE_INIT_QOM);
>  
>      do {} while (g_main_context_iteration(NULL, false));
>  
> diff --git a/util/throttle.c b/util/throttle.c
> index b2a52b8b34..99fd73157b 100644
> --- a/util/throttle.c
> +++ b/util/throttle.c
> @@ -502,3 +502,154 @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
>      }
>  }
>  
> +/* return a ThrottleConfig based on the options in a ThrottleLimits
> + *
> + * @arg:    the ThrottleLimits object to read from
> + * @cfg:    the ThrottleConfig to edit
> + * @errp:   error object
> + */
> +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
> +                               Error **errp)
> +{
> +    if (arg->has_bps_total) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total;
> +    }
> +    if (arg->has_bps_read) {
> +        cfg->buckets[THROTTLE_BPS_READ].avg  = arg->bps_read;
> +    }
> +    if (arg->has_bps_write) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write;
> +    }
> +
> +    if (arg->has_iops_total) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total;
> +    }
> +    if (arg->has_iops_read) {
> +        cfg->buckets[THROTTLE_OPS_READ].avg  = arg->iops_read;
> +    }
> +    if (arg->has_iops_write) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write;
> +    }
> +
> +    if (arg->has_bps_total_max) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max;
> +    }
> +    if (arg->has_bps_read_max) {
> +        cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max;
> +    }
> +    if (arg->has_bps_write_max) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max;
> +    }
> +    if (arg->has_iops_total_max) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max;
> +    }
> +    if (arg->has_iops_read_max) {
> +        cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max;
> +    }
> +    if (arg->has_iops_write_max) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max;
> +    }
> +
> +    if (arg->has_bps_total_max_length) {
> +        if (arg->bps_total_max_length > UINT_MAX) {
> +            error_setg(errp, "bps-total-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length;
> +    }
> +    if (arg->has_bps_read_max_length) {
> +        if (arg->bps_read_max_length > UINT_MAX) {
> +            error_setg(errp, "bps-read-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length;
> +    }
> +    if (arg->has_bps_write_max_length) {
> +        if (arg->bps_write_max_length > UINT_MAX) {
> +            error_setg(errp, "bps-write-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length;
> +    }
> +    if (arg->has_iops_total_max_length) {
> +        if (arg->iops_total_max_length > UINT_MAX) {
> +            error_setg(errp, "iops-total-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length;
> +    }
> +    if (arg->has_iops_read_max_length) {
> +        if (arg->iops_read_max_length > UINT_MAX) {
> +            error_setg(errp, "iops-read-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length;
> +    }
> +    if (arg->has_iops_write_max_length) {
> +        if (arg->iops_write_max_length > UINT_MAX) {
> +            error_setg(errp, "iops-write-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length;
> +    }
> +
> +    if (arg->has_iops_size) {
> +        cfg->op_size = arg->iops_size;
> +    }
> +
> +    throttle_is_valid(cfg, errp);
> +}
> +
> +/* write the options of a ThrottleConfig to a ThrottleLimits
> + *
> + * @cfg:    the ThrottleConfig to read from
> + * @var:    the ThrottleLimits to write to
> + */
> +void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits *var)
> +{
> +    var->bps_total               = cfg->buckets[THROTTLE_BPS_TOTAL].avg;
> +    var->bps_read                = cfg->buckets[THROTTLE_BPS_READ].avg;
> +    var->bps_write               = cfg->buckets[THROTTLE_BPS_WRITE].avg;
> +    var->iops_total              = cfg->buckets[THROTTLE_OPS_TOTAL].avg;
> +    var->iops_read               = cfg->buckets[THROTTLE_OPS_READ].avg;
> +    var->iops_write              = cfg->buckets[THROTTLE_OPS_WRITE].avg;
> +    var->bps_total_max           = cfg->buckets[THROTTLE_BPS_TOTAL].max;
> +    var->bps_read_max            = cfg->buckets[THROTTLE_BPS_READ].max;
> +    var->bps_write_max           = cfg->buckets[THROTTLE_BPS_WRITE].max;
> +    var->iops_total_max          = cfg->buckets[THROTTLE_OPS_TOTAL].max;
> +    var->iops_read_max           = cfg->buckets[THROTTLE_OPS_READ].max;
> +    var->iops_write_max          = cfg->buckets[THROTTLE_OPS_WRITE].max;
> +    var->bps_total_max_length    = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length;
> +    var->bps_read_max_length     = cfg->buckets[THROTTLE_BPS_READ].burst_length;
> +    var->bps_write_max_length    = cfg->buckets[THROTTLE_BPS_WRITE].burst_length;
> +    var->iops_total_max_length   = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length;
> +    var->iops_read_max_length    = cfg->buckets[THROTTLE_OPS_READ].burst_length;
> +    var->iops_write_max_length   = cfg->buckets[THROTTLE_OPS_WRITE].burst_length;
> +    var->iops_size               = cfg->op_size;
> +
> +    var->has_bps_total = true;
> +    var->has_bps_read = true;
> +    var->has_bps_write = true;
> +    var->has_iops_total = true;
> +    var->has_iops_read = true;
> +    var->has_iops_write = true;
> +    var->has_bps_total_max = true;
> +    var->has_bps_read_max = true;
> +    var->has_bps_write_max = true;
> +    var->has_iops_total_max = true;
> +    var->has_iops_read_max = true;
> +    var->has_iops_write_max = true;
> +    var->has_bps_read_max_length = true;
> +    var->has_bps_total_max_length = true;
> +    var->has_bps_write_max_length = true;
> +    var->has_iops_total_max_length = true;
> +    var->has_iops_read_max_length = true;
> +    var->has_iops_write_max_length = true;
> +    var->has_iops_size = true;
> +}
> -- 
> 2.11.0
> 

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver Manos Pitsidianakis
@ 2017-08-01 16:14   ` Stefan Hajnoczi
  2017-08-03  8:07   ` Kevin Wolf
  2017-08-08 13:13   ` Alberto Garcia
  2 siblings, 0 replies; 43+ messages in thread
From: Stefan Hajnoczi @ 2017-08-01 16:14 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Alberto Garcia, qemu-block

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

On Mon, Jul 31, 2017 at 12:54:41PM +0300, Manos Pitsidianakis wrote:
> +static int throttle_configure_tgm(BlockDriverState *bs,
> +                                  ThrottleGroupMember *tgm,
> +                                  QDict *options, Error **errp)
> +{
> +    int ret;
> +    ThrottleConfig cfg;
> +    const char *group_name = NULL;
> +    Error *local_err = NULL;
> +    QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);

If there is no scenario where this can fail please use error_abort.

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

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

* Re: [Qemu-devel] [PATCH v3 6/7] block: add BlockDevOptionsThrottle to QAPI
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 6/7] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
@ 2017-08-01 16:16   ` Stefan Hajnoczi
  0 siblings, 0 replies; 43+ messages in thread
From: Stefan Hajnoczi @ 2017-08-01 16:16 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Alberto Garcia, qemu-block

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

On Mon, Jul 31, 2017 at 12:54:42PM +0300, Manos Pitsidianakis wrote:
> @@ -3095,6 +3096,22 @@
>              '*tls-creds': 'str' } }
>  
>  ##
> +# @BlockdevOptionsThrottle:
> +#
> +# Driver specific block device options for the throttle driver
> +#
> +# @throttle-group:   the name of the throttle-group object to use. It will be
> +#                    created if it doesn't already exist
> +# @file:             reference to or definition of the data source block device
> +# @limits:           ThrottleLimits options
> +# Since: 2.11
> +##
> +{ 'struct': 'BlockdevOptionsThrottle',
> +  'data': { '*throttle-group': 'str',
> +            'file' : 'BlockdevRef',
> +            '*limits' : 'ThrottleLimits'
> +             } }

What happens when throttle-group isn't given?  Please document it.

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

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-01 15:47   ` Stefan Hajnoczi
@ 2017-08-01 16:49     ` Manos Pitsidianakis
  2017-08-02 10:39       ` Stefan Hajnoczi
  0 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-01 16:49 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: qemu-devel, Kevin Wolf, Alberto Garcia, qemu-block

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

On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
>On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
>> ThrottleGroup is converted to an object. This will allow the future
>> throttle block filter drive easy creation and configuration of throttle
>> groups in QMP and cli.
>>
>> A new QAPI struct, ThrottleLimits, is introduced to provide a shared
>> struct for all throttle configuration needs in QMP.
>>
>> ThrottleGroups can be created via CLI as
>>     -object throttle-group,id=foo,x-iops-total=100,x-..
>> where x-* are individual limit properties. Since we can't add non-scalar
>> properties in -object this interface must be used instead. However,
>> setting these properties must be disabled after initialization because
>> certain combinations of limits are forbidden and thus configuration
>> changes should be done in one transaction. The individual properties
>> will go away when support for non-scalar values in CLI is implemented
>> and thus are marked as experimental.
>>
>> ThrottleGroup also has a `limits` property that uses the ThrottleLimits
>> struct.  It can be used to create ThrottleGroups or set the
>> configuration in existing groups as follows:
>>
>> { "execute": "object-add",
>>   "arguments": {
>>     "qom-type": "throttle-group",
>>     "id": "foo",
>>     "props" : {
>>       "limits": {
>>           "iops-total": 100
>>       }
>>     }
>>   }
>> }
>> { "execute" : "qom-set",
>>     "arguments" : {
>>         "path" : "foo",
>>         "property" : "limits",
>>         "value" : {
>>             "iops-total" : 99
>>         }
>>     }
>> }
>>
>> This also means a group's configuration can be fetched with qom-get.
>>
>> ThrottleGroups can be anonymous which means they can't get accessed by
>> other users ie they will always be units instead of group (Because they
>> have one ThrottleGroupMember).
>
>blockdev.c automatically assigns -drive id= to the group name if
>throttling.group= wasn't given.  So who will use anonymous single-drive
>mode?

Manual filter nodes. It's possible to not pass a group name value and 
the resulting group will be anonymous. Are you suggesting to move this 
change to the throttle filter patch?

>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>> ---
>> Notes:
>>     Note: I tested Markus Armbruster's patch in <87wp7fghi9.fsf@dusky.pond.sub.org>
>>     on master and I can use this syntax successfuly:
>>     -object '{ "qom-type" : "throttle-group", "id" : "foo", "props" : { "limits" \
>>     : { "iops-total" : 1000 } } }'
>>     If this gets merged using -object will be a little more verbose but at least we
>>     won't have seperate properties, which is a good thing, so the x-* should be
>>     dropped.
>>
>>  block/throttle-groups.c         | 402 ++++++++++++++++++++++++++++++++++++----
>>  include/block/throttle-groups.h |   3 +
>>  include/qemu/throttle-options.h |  59 ++++--
>>  include/qemu/throttle.h         |   3 +
>>  qapi/block-core.json            |  48 +++++
>>  tests/test-throttle.c           |   1 +
>>  util/throttle.c                 | 151 +++++++++++++++
>>  7 files changed, 617 insertions(+), 50 deletions(-)
>>
>> diff --git a/block/throttle-groups.c b/block/throttle-groups.c
>> index f711a3dc62..b9c5036e44 100644
>> --- a/block/throttle-groups.c
>> +++ b/block/throttle-groups.c
>> @@ -25,9 +25,17 @@
>>  #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"
>> +#include "qapi-visit.h"
>> +#include "qom/object.h"
>> +#include "qom/object_interfaces.h"
>> +
>> +static void throttle_group_obj_init(Object *obj);
>> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
>>
>>  /* The ThrottleGroup structure (with its ThrottleState) is shared
>>   * among different ThrottleGroupMembers and it's independent from
>> @@ -54,6 +62,10 @@
>>   * that ThrottleGroupMember has throttled requests in the queue.
>>   */
>>  typedef struct ThrottleGroup {
>> +    Object parent_obj;
>> +
>> +    /* refuse individual property change if initialization is complete */
>> +    bool is_initialized;
>>      char *name; /* This is constant during the lifetime of the group */
>>
>>      QemuMutex lock; /* This lock protects the following four fields */
>> @@ -63,8 +75,7 @@ typedef struct ThrottleGroup {
>>      bool any_timer_armed[2];
>>      QEMUClockType clock_type;
>>
>> -    /* These two are protected by the global throttle_groups_lock */
>> -    unsigned refcount;
>> +    /* This is protected by the global throttle_groups_lock */
>>      QTAILQ_ENTRY(ThrottleGroup) list;
>>  } ThrottleGroup;
>>
>> @@ -77,7 +88,8 @@ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
>>   * If no ThrottleGroup is found with the given name a new one is
>>   * created.
>>   *
>> - * @name: the name of the ThrottleGroup
>> + * @name: the name of the ThrottleGroup, NULL means a new anonymous group will
>> + *        be created.
>>   * @ret:  the ThrottleState member of the ThrottleGroup
>>   */
>>  ThrottleState *throttle_group_incref(const char *name)
>> @@ -87,32 +99,30 @@ ThrottleState *throttle_group_incref(const char *name)
>>
>>      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)) {
>> -            tg = iter;
>> -            break;
>> +    if (name) {
>> +        /* Look for an existing group with that name */
>> +        QTAILQ_FOREACH(iter, &throttle_groups, list) {
>> +            if (!g_strcmp0(name, iter->name)) {
>> +                tg = iter;
>> +                break;
>> +            }
>>          }
>>      }
>>
>>      /* Create a new one if not found */
>>      if (!tg) {
>> -        tg = g_new0(ThrottleGroup, 1);
>> +        /* new ThrottleGroup obj will have a refcnt = 1 */
>> +        tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
>>          tg->name = g_strdup(name);
>> -        tg->clock_type = QEMU_CLOCK_REALTIME;
>> -
>> -        if (qtest_enabled()) {
>> -            /* For testing block IO throttling only */
>> -            tg->clock_type = QEMU_CLOCK_VIRTUAL;
>> -        }
>> -        qemu_mutex_init(&tg->lock);
>> -        throttle_init(&tg->ts);
>> -        QLIST_INIT(&tg->head);
>> -
>> -        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
>> +        throttle_group_obj_complete((UserCreatable *)tg, &error_abort);
>>      }
>>
>> -    tg->refcount++;
>> +    qemu_mutex_lock(&tg->lock);
>> +    if (!QLIST_EMPTY(&tg->head)) {
>> +        /* only ref if the group is not empty */
>> +        object_ref(OBJECT(tg));
>> +    }
>> +    qemu_mutex_unlock(&tg->lock);
>
>How do the refcounts work in various cases (legacy -drive
>throttling.group and -object throttle-group with 0, 1, or multiple
>drives)?
>
>It's not obvious to me that this code works in all cases.

Object is created with object_new(): ref count is 1
Each time we call throttle_group_incref() to add a new member to the 
group, we increase the ref count by 1. We skip the first time we do that 
because there's already a reference. When all members are removed, 
object is deleted.

>>
>>      qemu_mutex_unlock(&throttle_groups_lock);
>>
>> @@ -129,15 +139,7 @@ ThrottleState *throttle_group_incref(const char *name)
>>  void throttle_group_unref(ThrottleState *ts)
>>  {
>>      ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
>> -
>> -    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);
>> +    object_unref(OBJECT(tg));
>>  }
>>
>>  /* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
>> @@ -572,9 +574,347 @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
>>      throttle_timers_detach_aio_context(tt);
>>  }
>>
>> +#undef THROTTLE_OPT_PREFIX
>> +#define THROTTLE_OPT_PREFIX "x-"
>> +#define DOUBLE 0
>> +#define UINT64 1
>> +#define UNSIGNED 2
>> +
>> +typedef struct {
>> +    const char *name;
>> +    BucketType type;
>> +    int data_type;
>> +    const ptrdiff_t offset; /* offset in LeakyBucket struct. */
>> +} ThrottleParamInfo;
>> +
>> +static ThrottleParamInfo properties[] = {
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
>> +    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,
>> +    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
>> +    THROTTLE_OPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
>> +    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,
>> +    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, max),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
>> +    THROTTLE_OPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
>> +    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,
>> +    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
>> +    THROTTLE_OPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
>> +    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,
>> +    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
>> +    THROTTLE_BPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
>> +    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,
>> +    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, max),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
>> +    THROTTLE_BPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
>> +    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,
>> +    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
>> +    THROTTLE_BPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
>> +},
>> +{
>> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
>> +    0, UINT64, offsetof(ThrottleConfig, op_size),
>> +}
>> +};
>> +
>> +static void throttle_group_obj_init(Object *obj)
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +
>> +    tg->clock_type = QEMU_CLOCK_REALTIME;
>> +    if (qtest_enabled()) {
>> +        /* For testing block IO throttling only */
>> +        tg->clock_type = QEMU_CLOCK_VIRTUAL;
>> +    }
>> +    tg->is_initialized = false;
>> +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
>
>Is the lock taken when the object-add QMP command or -object
>command-line argument are used?
>
>In any case, please do not assume that throttle_groups_lock is held for
>Object->init().  It should be possible to create new instances at any
>time.

Hm, isn't the global lock held in both cases? And in the third case 
where we create the object from throttle_group_incref() we hold the 
throttle_groups_lock.


>> +    qemu_mutex_init(&tg->lock);
>> +    throttle_init(&tg->ts);
>> +    QLIST_INIT(&tg->head);
>> +}
>> +
>> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj), *iter;
>> +    ThrottleConfig *cfg = &tg->ts.cfg;
>> +
>> +    /* set group name to object id if it exists */
>> +    if (!tg->name && tg->parent_obj.parent) {
>> +        tg->name = object_get_canonical_path_component(OBJECT(obj));
>> +    }
>> +
>> +    if (tg->name) {
>> +        /* error if name is duplicate */
>> +        QTAILQ_FOREACH(iter, &throttle_groups, list) {
>
>Same locking issue: is throttle_groups_lock held here in all cases?  I
>think it's not held in the object-add QMP and -object throttle-group
>cases.
>
>> +            if (!g_strcmp0(tg->name, iter->name) && tg != iter) {
>> +                error_setg(errp, "A group with this name already exists");
>> +                return;
>> +            }
>> +        }
>> +    }
>> +
>> +    /* unfix buckets to check validity */
>> +    throttle_get_config(&tg->ts, cfg);
>> +    if (!throttle_is_valid(cfg, errp)) {
>> +        return;
>> +    }
>> +    /* fix buckets again */
>> +    throttle_config(&tg->ts, tg->clock_type, cfg);
>> +
>> +    tg->is_initialized = true;
>> +}
>> +
>> +static void throttle_group_obj_finalize(Object *obj)
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +    qemu_mutex_lock(&throttle_groups_lock);
>> +    QTAILQ_REMOVE(&throttle_groups, tg, list);
>> +    qemu_mutex_unlock(&throttle_groups_lock);
>> +    qemu_mutex_destroy(&tg->lock);
>> +    g_free(tg->name);
>> +}
>> +
>> +static void throttle_group_set(Object *obj, Visitor *v, const char * name,
>> +        void *opaque, Error **errp)
>> +
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +    ThrottleConfig cfg;
>> +    ThrottleParamInfo *info = opaque;
>> +    ThrottleLimits *arg = NULL;
>> +    Error *local_err = NULL;
>> +    int64_t value;
>> +
>> +    /* If we have finished initialization, don't accept individual property
>> +     * changes through QOM. Throttle configuration limits must be set in one
>> +     * transaction, as certain combinations are invalid.
>> +     */
>> +    if (tg->is_initialized) {
>> +        error_setg(&local_err, "Property cannot be set after initialization");
>> +        goto fail;
>> +    }
>> +
>> +    visit_type_int64(v, name, &value, &local_err);
>> +    if (local_err) {
>> +        goto fail;
>> +    }
>> +    if (value < 0) {
>> +        error_setg(&local_err, "Property values cannot be negative");
>> +        goto fail;
>> +    }
>> +
>> +    cfg = tg->ts.cfg;
>> +    switch (info->data_type) {
>> +    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:
>> +        {
>> +            if (value > UINT_MAX) {
>> +                error_setg(&local_err, "%s value must be in the"
>> +                                       "range [0, %u]", info->name, UINT_MAX);
>> +                goto fail;
>> +            }
>> +            unsigned *field = (void *)&cfg.buckets[info->type] + info->offset;
>> +            *field = value;
>> +        }
>> +    }
>> +
>> +    tg->ts.cfg = cfg;
>> +    goto ret;
>> +
>> +fail:
>> +    error_propagate(errp, local_err);
>> +ret:
>> +    g_free(arg);
>> +    return;
>
>arg is unused, please drop it.

Oops!
>
>> +
>> +}
>> +
>> +static void throttle_group_get(Object *obj, Visitor *v, const char *name,
>> +        void *opaque, Error **errp)
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +    ThrottleConfig cfg;
>> +    ThrottleParamInfo *info = opaque;
>> +    int64_t value;
>> +
>> +    cfg = tg->ts.cfg;
>> +    switch (info->data_type) {
>> +    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_set_limits(Object *obj, Visitor *v,
>> +                                      const char *name, void *opaque,
>> +                                      Error **errp)
>> +
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +    ThrottleConfig cfg;
>> +    ThrottleLimits *arg = NULL;
>> +    Error *local_err = NULL;
>> +
>> +    arg = g_new0(ThrottleLimits, 1);
>
>Why does ThrottleLimits need to be allocated on the heap?

It is passed to visit_type_ThrottleLimits which needs a double pointer.  
The alternative would be:

    ThrottleLimits arg = { 0 }, *p = &arg;
    visit_type_ThrottleLimits(v, name, &p, &local_err);

Which didn't seem very pretty at the time. Is it a problem?

>
>> +    visit_type_ThrottleLimits(v, name, &arg, &local_err);
>> +    if (local_err) {
>> +        goto fail;
>> +    }
>> +    qemu_mutex_lock(&tg->lock);
>> +    throttle_get_config(&tg->ts, &cfg);
>> +    throttle_limits_to_config(arg, &cfg, &local_err);
>> +    if (local_err) {
>> +        qemu_mutex_unlock(&tg->lock);
>> +        goto fail;
>> +    }
>> +    throttle_config(&tg->ts, tg->clock_type, &cfg);
>> +    qemu_mutex_unlock(&tg->lock);
>> +
>> +    goto ret;
>
>error_propagate() is a nop if local_err is NULL so this goto is
>unnecessary.
>
>> +
>> +fail:
>> +    error_propagate(errp, local_err);
>> +ret:
>> +    g_free(arg);
>> +    return;
>> +}
>> +
>> +static void throttle_group_get_limits(Object *obj, Visitor *v,
>> +                                      const char *name, void *opaque,
>> +                                      Error **errp)
>> +{
>> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> +    ThrottleConfig cfg;
>> +    ThrottleLimits *arg = NULL;
>> +
>> +    arg = g_new0(ThrottleLimits, 1);
>> +    qemu_mutex_lock(&tg->lock);
>> +    throttle_get_config(&tg->ts, &cfg);
>> +    qemu_mutex_unlock(&tg->lock);
>> +    throttle_config_to_throttle_limits(&cfg, arg);
>> +    visit_type_ThrottleLimits(v, name, &arg, errp);
>> +    g_free(arg);
>> +}
>> +
>> +static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data)
>> +{
>> +    size_t i = 0;
>> +    UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
>> +
>> +    ucc->complete = throttle_group_obj_complete;
>> +    /* individual properties */
>> +    for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) {
>> +        object_class_property_add(klass,
>> +                properties[i].name,
>> +                "int",
>> +                throttle_group_get,
>> +                throttle_group_set,
>> +                NULL, &properties[i],
>> +                &error_abort);
>> +    }
>> +
>> +    /* ThrottleLimits */
>> +    object_class_property_add(klass,
>> +                              "limits", "ThrottleLimits",
>> +                              throttle_group_get_limits,
>> +                              throttle_group_set_limits,
>> +                              NULL, NULL,
>> +                              &error_abort);
>> +}
>> +
>> +static const TypeInfo throttle_group_info = {
>> +   .name = TYPE_THROTTLE_GROUP,
>> +   .parent = TYPE_OBJECT,
>> +   .class_init = throttle_group_obj_class_init,
>> +   .instance_size = sizeof(ThrottleGroup),
>> +   .instance_init = throttle_group_obj_init,
>> +   .instance_finalize = throttle_group_obj_finalize,
>> +   .interfaces = (InterfaceInfo[]) {
>> +       { TYPE_USER_CREATABLE },
>> +       { }
>> +   },
>> +};
>> +
>>  static void throttle_groups_init(void)
>>  {
>>      qemu_mutex_init(&throttle_groups_lock);
>> +    type_register_static(&throttle_group_info);
>>  }
>>
>> -block_init(throttle_groups_init);
>> +type_init(throttle_groups_init);
>> diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
>> index a0f27cac63..82f030523f 100644
>> --- a/include/block/throttle-groups.h
>> +++ b/include/block/throttle-groups.h
>> @@ -53,6 +53,9 @@ typedef struct ThrottleGroupMember {
>>
>>  } ThrottleGroupMember;
>>
>> +#define TYPE_THROTTLE_GROUP "throttle-group"
>> +#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), TYPE_THROTTLE_GROUP)
>> +
>>  const char *throttle_group_get_name(ThrottleGroupMember *tgm);
>>
>>  ThrottleState *throttle_group_incref(const char *name);
>> diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
>> index 3133d1ca40..182b7896e1 100644
>> --- a/include/qemu/throttle-options.h
>> +++ b/include/qemu/throttle-options.h
>> @@ -10,81 +10,102 @@
>>  #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 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",\
>>          }
>> diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
>> index d056008c18..17e750b12d 100644
>> --- a/include/qemu/throttle.h
>> +++ b/include/qemu/throttle.h
>> @@ -152,5 +152,8 @@ bool throttle_schedule_timer(ThrottleState *ts,
>>                               bool is_write);
>>
>>  void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
>> +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
>> +                               Error **errp);
>> +void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits *var);
>>
>>  #endif
>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>> index 833c602150..0bdc69aa5f 100644
>> --- a/qapi/block-core.json
>> +++ b/qapi/block-core.json
>> @@ -1905,6 +1905,54 @@
>>              '*iops_size': 'int', '*group': 'str' } }
>>
>>  ##
>> +# @ThrottleLimits:
>> +#
>> +# Limit parameters for throttling.
>> +# Since some limit combinations are illegal, limits should always be set in one
>> +# transaction. All fields are optional. When setting limits, if a field is
>> +# missing the current value is not changed.
>> +#
>> +# @iops-total:             limit total I/O operations per second
>> +# @iops-total-max:         I/O operations burst
>> +# @iops-total-max-length:  length of the iops-total-max burst period, in seconds
>> +#                          It must only be set if @iops-total-max is set as well.
>> +# @iops-read:              limit read operations per second
>> +# @iops-read-max:          I/O operations read burst
>> +# @iops-read-max-length:   length of the iops-read-max burst period, in seconds
>> +#                          It must only be set if @iops-read-max is set as well.
>> +# @iops-write:             limit write operations per second
>> +# @iops-write-max:         I/O operations write burst
>> +# @iops-write-max-length:  length of the iops-write-max burst period, in seconds
>> +#                          It must only be set if @iops-write-max is set as well.
>> +# @bps-total:              limit total bytes per second
>> +# @bps-total-max:          total bytes burst
>> +# @bps-total-max-length:   length of the bps-total-max burst period, in seconds.
>> +#                          It must only be set if @bps-total-max is set as well.
>> +# @bps-read:               limit read bytes per second
>> +# @bps-read-max:           total bytes read burst
>> +# @bps-read-max-length:    length of the bps-read-max burst period, in seconds
>> +#                          It must only be set if @bps-read-max is set as well.
>> +# @bps-write:              limit write bytes per second
>> +# @bps-write-max:          total bytes write burst
>> +# @bps-write-max-length:   length of the bps-write-max burst period, in seconds
>> +#                          It must only be set if @bps-write-max is set as well.
>> +# @iops-size:              when limiting by iops max size of an I/O in bytes
>> +#
>> +# Since: 2.11
>> +##
>> +{ 'struct': 'ThrottleLimits',
>> +  'data': { '*iops-total' : 'int', '*iops-total-max' : 'int',
>> +            '*iops-total-max-length' : 'int', '*iops-read' : 'int',
>> +            '*iops-read-max' : 'int', '*iops-read-max-length' : 'int',
>> +            '*iops-write' : 'int', '*iops-write-max' : 'int',
>> +            '*iops-write-max-length' : 'int', '*bps-total' : 'int',
>> +            '*bps-total-max' : 'int', '*bps-total-max-length' : 'int',
>> +            '*bps-read' : 'int', '*bps-read-max' : 'int',
>> +            '*bps-read-max-length' : 'int', '*bps-write' : 'int',
>> +            '*bps-write-max' : 'int', '*bps-write-max-length' : 'int',
>> +            '*iops-size' : 'int' } }
>> +
>> +##
>>  # @block-stream:
>>  #
>>  # Copy data from a backing file into a block device.
>> diff --git a/tests/test-throttle.c b/tests/test-throttle.c
>> index 57cf5ba711..0ea9093eee 100644
>> --- a/tests/test-throttle.c
>> +++ b/tests/test-throttle.c
>> @@ -662,6 +662,7 @@ int main(int argc, char **argv)
>>      qemu_init_main_loop(&error_fatal);
>>      ctx = qemu_get_aio_context();
>>      bdrv_init();
>> +    module_call_init(MODULE_INIT_QOM);
>>
>>      do {} while (g_main_context_iteration(NULL, false));
>>
>> diff --git a/util/throttle.c b/util/throttle.c
>> index b2a52b8b34..99fd73157b 100644
>> --- a/util/throttle.c
>> +++ b/util/throttle.c
>> @@ -502,3 +502,154 @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
>>      }
>>  }
>>
>> +/* return a ThrottleConfig based on the options in a ThrottleLimits
>> + *
>> + * @arg:    the ThrottleLimits object to read from
>> + * @cfg:    the ThrottleConfig to edit
>> + * @errp:   error object
>> + */
>> +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
>> +                               Error **errp)
>> +{
>> +    if (arg->has_bps_total) {
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total;
>> +    }
>> +    if (arg->has_bps_read) {
>> +        cfg->buckets[THROTTLE_BPS_READ].avg  = arg->bps_read;
>> +    }
>> +    if (arg->has_bps_write) {
>> +        cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write;
>> +    }
>> +
>> +    if (arg->has_iops_total) {
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total;
>> +    }
>> +    if (arg->has_iops_read) {
>> +        cfg->buckets[THROTTLE_OPS_READ].avg  = arg->iops_read;
>> +    }
>> +    if (arg->has_iops_write) {
>> +        cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write;
>> +    }
>> +
>> +    if (arg->has_bps_total_max) {
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max;
>> +    }
>> +    if (arg->has_bps_read_max) {
>> +        cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max;
>> +    }
>> +    if (arg->has_bps_write_max) {
>> +        cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max;
>> +    }
>> +    if (arg->has_iops_total_max) {
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max;
>> +    }
>> +    if (arg->has_iops_read_max) {
>> +        cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max;
>> +    }
>> +    if (arg->has_iops_write_max) {
>> +        cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max;
>> +    }
>> +
>> +    if (arg->has_bps_total_max_length) {
>> +        if (arg->bps_total_max_length > UINT_MAX) {
>> +            error_setg(errp, "bps-total-max-length value must be in"
>> +                    " the range [0, %u]", UINT_MAX);
>> +            return;
>> +        }
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length;
>> +    }
>> +    if (arg->has_bps_read_max_length) {
>> +        if (arg->bps_read_max_length > UINT_MAX) {
>> +            error_setg(errp, "bps-read-max-length value must be in"
>> +                    " the range [0, %u]", UINT_MAX);
>> +            return;
>> +        }
>> +        cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length;
>> +    }
>> +    if (arg->has_bps_write_max_length) {
>> +        if (arg->bps_write_max_length > UINT_MAX) {
>> +            error_setg(errp, "bps-write-max-length value must be in"
>> +                    " the range [0, %u]", UINT_MAX);
>> +            return;
>> +        }
>> +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length;
>> +    }
>> +    if (arg->has_iops_total_max_length) {
>> +        if (arg->iops_total_max_length > UINT_MAX) {
>> +            error_setg(errp, "iops-total-max-length value must be in"
>> +                    " the range [0, %u]", UINT_MAX);
>> +            return;
>> +        }
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length;
>> +    }
>> +    if (arg->has_iops_read_max_length) {
>> +        if (arg->iops_read_max_length > UINT_MAX) {
>> +            error_setg(errp, "iops-read-max-length value must be in"
>> +                    " the range [0, %u]", UINT_MAX);
>> +            return;
>> +        }
>> +        cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length;
>> +    }
>> +    if (arg->has_iops_write_max_length) {
>> +        if (arg->iops_write_max_length > UINT_MAX) {
>> +            error_setg(errp, "iops-write-max-length value must be in"
>> +                    " the range [0, %u]", UINT_MAX);
>> +            return;
>> +        }
>> +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length;
>> +    }
>> +
>> +    if (arg->has_iops_size) {
>> +        cfg->op_size = arg->iops_size;
>> +    }
>> +
>> +    throttle_is_valid(cfg, errp);
>> +}
>> +
>> +/* write the options of a ThrottleConfig to a ThrottleLimits
>> + *
>> + * @cfg:    the ThrottleConfig to read from
>> + * @var:    the ThrottleLimits to write to
>> + */
>> +void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits *var)
>> +{
>> +    var->bps_total               = cfg->buckets[THROTTLE_BPS_TOTAL].avg;
>> +    var->bps_read                = cfg->buckets[THROTTLE_BPS_READ].avg;
>> +    var->bps_write               = cfg->buckets[THROTTLE_BPS_WRITE].avg;
>> +    var->iops_total              = cfg->buckets[THROTTLE_OPS_TOTAL].avg;
>> +    var->iops_read               = cfg->buckets[THROTTLE_OPS_READ].avg;
>> +    var->iops_write              = cfg->buckets[THROTTLE_OPS_WRITE].avg;
>> +    var->bps_total_max           = cfg->buckets[THROTTLE_BPS_TOTAL].max;
>> +    var->bps_read_max            = cfg->buckets[THROTTLE_BPS_READ].max;
>> +    var->bps_write_max           = cfg->buckets[THROTTLE_BPS_WRITE].max;
>> +    var->iops_total_max          = cfg->buckets[THROTTLE_OPS_TOTAL].max;
>> +    var->iops_read_max           = cfg->buckets[THROTTLE_OPS_READ].max;
>> +    var->iops_write_max          = cfg->buckets[THROTTLE_OPS_WRITE].max;
>> +    var->bps_total_max_length    = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length;
>> +    var->bps_read_max_length     = cfg->buckets[THROTTLE_BPS_READ].burst_length;
>> +    var->bps_write_max_length    = cfg->buckets[THROTTLE_BPS_WRITE].burst_length;
>> +    var->iops_total_max_length   = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length;
>> +    var->iops_read_max_length    = cfg->buckets[THROTTLE_OPS_READ].burst_length;
>> +    var->iops_write_max_length   = cfg->buckets[THROTTLE_OPS_WRITE].burst_length;
>> +    var->iops_size               = cfg->op_size;
>> +
>> +    var->has_bps_total = true;
>> +    var->has_bps_read = true;
>> +    var->has_bps_write = true;
>> +    var->has_iops_total = true;
>> +    var->has_iops_read = true;
>> +    var->has_iops_write = true;
>> +    var->has_bps_total_max = true;
>> +    var->has_bps_read_max = true;
>> +    var->has_bps_write_max = true;
>> +    var->has_iops_total_max = true;
>> +    var->has_iops_read_max = true;
>> +    var->has_iops_write_max = true;
>> +    var->has_bps_read_max_length = true;
>> +    var->has_bps_total_max_length = true;
>> +    var->has_bps_write_max_length = true;
>> +    var->has_iops_total_max_length = true;
>> +    var->has_iops_read_max_length = true;
>> +    var->has_iops_write_max_length = true;
>> +    var->has_iops_size = true;
>> +}
>> --
>> 2.11.0
>>



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

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-01 16:49     ` Manos Pitsidianakis
@ 2017-08-02 10:39       ` Stefan Hajnoczi
  2017-08-02 10:57         ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Hajnoczi @ 2017-08-02 10:39 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel, Kevin Wolf, Alberto Garcia, qemu-block

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

On Tue, Aug 01, 2017 at 07:49:33PM +0300, Manos Pitsidianakis wrote:
> On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
> > On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
> > > ThrottleGroup is converted to an object. This will allow the future
> > > throttle block filter drive easy creation and configuration of throttle
> > > groups in QMP and cli.
> > > 
> > > A new QAPI struct, ThrottleLimits, is introduced to provide a shared
> > > struct for all throttle configuration needs in QMP.
> > > 
> > > ThrottleGroups can be created via CLI as
> > >     -object throttle-group,id=foo,x-iops-total=100,x-..
> > > where x-* are individual limit properties. Since we can't add non-scalar
> > > properties in -object this interface must be used instead. However,
> > > setting these properties must be disabled after initialization because
> > > certain combinations of limits are forbidden and thus configuration
> > > changes should be done in one transaction. The individual properties
> > > will go away when support for non-scalar values in CLI is implemented
> > > and thus are marked as experimental.
> > > 
> > > ThrottleGroup also has a `limits` property that uses the ThrottleLimits
> > > struct.  It can be used to create ThrottleGroups or set the
> > > configuration in existing groups as follows:
> > > 
> > > { "execute": "object-add",
> > >   "arguments": {
> > >     "qom-type": "throttle-group",
> > >     "id": "foo",
> > >     "props" : {
> > >       "limits": {
> > >           "iops-total": 100
> > >       }
> > >     }
> > >   }
> > > }
> > > { "execute" : "qom-set",
> > >     "arguments" : {
> > >         "path" : "foo",
> > >         "property" : "limits",
> > >         "value" : {
> > >             "iops-total" : 99
> > >         }
> > >     }
> > > }
> > > 
> > > This also means a group's configuration can be fetched with qom-get.
> > > 
> > > ThrottleGroups can be anonymous which means they can't get accessed by
> > > other users ie they will always be units instead of group (Because they
> > > have one ThrottleGroupMember).
> > 
> > blockdev.c automatically assigns -drive id= to the group name if
> > throttling.group= wasn't given.  So who will use anonymous single-drive
> > mode?
> 
> Manual filter nodes. It's possible to not pass a group name value and the
> resulting group will be anonymous. Are you suggesting to move this change to
> the throttle filter patch?

What is the use case?  Human users will stick to the legacy syntax
because it's convenient.  Management tools will use the filter
explicitly in the future, and it's easy for them to choose a name.

Unless there is a need for this case I'd prefer to make the group name
mandatory.  That way there are less code paths to worry about.

> > > @@ -87,32 +99,30 @@ ThrottleState *throttle_group_incref(const char *name)
> > > 
> > >      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)) {
> > > -            tg = iter;
> > > -            break;
> > > +    if (name) {
> > > +        /* Look for an existing group with that name */
> > > +        QTAILQ_FOREACH(iter, &throttle_groups, list) {
> > > +            if (!g_strcmp0(name, iter->name)) {
> > > +                tg = iter;
> > > +                break;
> > > +            }
> > >          }
> > >      }
> > > 
> > >      /* Create a new one if not found */
> > >      if (!tg) {
> > > -        tg = g_new0(ThrottleGroup, 1);
> > > +        /* new ThrottleGroup obj will have a refcnt = 1 */
> > > +        tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
> > >          tg->name = g_strdup(name);
> > > -        tg->clock_type = QEMU_CLOCK_REALTIME;
> > > -
> > > -        if (qtest_enabled()) {
> > > -            /* For testing block IO throttling only */
> > > -            tg->clock_type = QEMU_CLOCK_VIRTUAL;
> > > -        }
> > > -        qemu_mutex_init(&tg->lock);
> > > -        throttle_init(&tg->ts);
> > > -        QLIST_INIT(&tg->head);
> > > -
> > > -        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> > > +        throttle_group_obj_complete((UserCreatable *)tg, &error_abort);
> > >      }
> > > 
> > > -    tg->refcount++;
> > > +    qemu_mutex_lock(&tg->lock);
> > > +    if (!QLIST_EMPTY(&tg->head)) {
> > > +        /* only ref if the group is not empty */
> > > +        object_ref(OBJECT(tg));
> > > +    }
> > > +    qemu_mutex_unlock(&tg->lock);
> > 
> > How do the refcounts work in various cases (legacy -drive
> > throttling.group and -object throttle-group with 0, 1, or multiple
> > drives)?
> > 
> > It's not obvious to me that this code works in all cases.
> 
> Object is created with object_new(): ref count is 1
> Each time we call throttle_group_incref() to add a new member to the group,
> we increase the ref count by 1. We skip the first time we do that because
> there's already a reference. When all members are removed, object is
> deleted.

If the ThrottleGroup was created with -object throttle-group it
shouldn't disappear when the last member is unregistered because the QOM
tree has 1 reference to the ThrottleGroup at all times due to
user_creatable_add_type():

  object_property_add_child(object_get_objects_root(),
                            id, obj, &local_err);

Is it okay to simplify the patch to:

  if (tg) {
      object_ref(OBJECT(tg));
  } else {
      tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
      ...
  }

That would be clearer - in both legs of the if statement we take a
reference to the object.  The if (!QLIST_EMPTY(tg->head)) check confuses
me.

> > > +static void throttle_group_obj_init(Object *obj)
> > > +{
> > > +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> > > +
> > > +    tg->clock_type = QEMU_CLOCK_REALTIME;
> > > +    if (qtest_enabled()) {
> > > +        /* For testing block IO throttling only */
> > > +        tg->clock_type = QEMU_CLOCK_VIRTUAL;
> > > +    }
> > > +    tg->is_initialized = false;
> > > +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> > 
> > Is the lock taken when the object-add QMP command or -object
> > command-line argument are used?
> > 
> > In any case, please do not assume that throttle_groups_lock is held for
> > Object->init().  It should be possible to create new instances at any
> > time.
> 
> Hm, isn't the global lock held in both cases? And in the third case where we
> create the object from throttle_group_incref() we hold the
> throttle_groups_lock.

At the moment I think throttle_groups_lock isn't strictly needed because
incref/decref callers hold the QEMU global mutex anyway.

But code accessing throttle_groups still has to be disciplined.  Since
throttle_groups_lock exists, please use it consistently in all code
paths.

Alternatively you could remove the lock and document that
throttle_groups is protected by the global mutex.  What we can't do is
sometimes use throttle_groups_lock and sometimes not use it.

> > > +static void throttle_group_set_limits(Object *obj, Visitor *v,
> > > +                                      const char *name, void *opaque,
> > > +                                      Error **errp)
> > > +
> > > +{
> > > +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> > > +    ThrottleConfig cfg;
> > > +    ThrottleLimits *arg = NULL;
> > > +    Error *local_err = NULL;
> > > +
> > > +    arg = g_new0(ThrottleLimits, 1);
> > 
> > Why does ThrottleLimits need to be allocated on the heap?
> 
> It is passed to visit_type_ThrottleLimits which needs a double pointer.  The
> alternative would be:
> 
>    ThrottleLimits arg = { 0 }, *p = &arg;
>    visit_type_ThrottleLimits(v, name, &p, &local_err);
> 
> Which didn't seem very pretty at the time. Is it a problem?

I was wondering because error code paths need to remember to free it.

Looking more closely at qapi-visit.c I think it *must* be heap-allocated
because qapi visit functions are allowed to fail and they free the
object on failure.

What you've done looks correct.

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

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-02 10:39       ` Stefan Hajnoczi
@ 2017-08-02 10:57         ` Manos Pitsidianakis
  2017-08-02 14:43           ` Stefan Hajnoczi
                             ` (2 more replies)
  0 siblings, 3 replies; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-02 10:57 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: qemu-devel, Kevin Wolf, Alberto Garcia, qemu-block

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

On Wed, Aug 02, 2017 at 11:39:22AM +0100, Stefan Hajnoczi wrote:
>On Tue, Aug 01, 2017 at 07:49:33PM +0300, Manos Pitsidianakis wrote:
>> On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
>> > On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
>> > > ThrottleGroup is converted to an object. This will allow the future
>> > > throttle block filter drive easy creation and configuration of throttle
>> > > groups in QMP and cli.
>> > >
>> > > A new QAPI struct, ThrottleLimits, is introduced to provide a shared
>> > > struct for all throttle configuration needs in QMP.
>> > >
>> > > ThrottleGroups can be created via CLI as
>> > >     -object throttle-group,id=foo,x-iops-total=100,x-..
>> > > where x-* are individual limit properties. Since we can't add non-scalar
>> > > properties in -object this interface must be used instead. However,
>> > > setting these properties must be disabled after initialization because
>> > > certain combinations of limits are forbidden and thus configuration
>> > > changes should be done in one transaction. The individual properties
>> > > will go away when support for non-scalar values in CLI is implemented
>> > > and thus are marked as experimental.
>> > >
>> > > ThrottleGroup also has a `limits` property that uses the ThrottleLimits
>> > > struct.  It can be used to create ThrottleGroups or set the
>> > > configuration in existing groups as follows:
>> > >
>> > > { "execute": "object-add",
>> > >   "arguments": {
>> > >     "qom-type": "throttle-group",
>> > >     "id": "foo",
>> > >     "props" : {
>> > >       "limits": {
>> > >           "iops-total": 100
>> > >       }
>> > >     }
>> > >   }
>> > > }
>> > > { "execute" : "qom-set",
>> > >     "arguments" : {
>> > >         "path" : "foo",
>> > >         "property" : "limits",
>> > >         "value" : {
>> > >             "iops-total" : 99
>> > >         }
>> > >     }
>> > > }
>> > >
>> > > This also means a group's configuration can be fetched with qom-get.
>> > >
>> > > ThrottleGroups can be anonymous which means they can't get accessed by
>> > > other users ie they will always be units instead of group (Because they
>> > > have one ThrottleGroupMember).
>> >
>> > blockdev.c automatically assigns -drive id= to the group name if
>> > throttling.group= wasn't given.  So who will use anonymous single-drive
>> > mode?
>>
>> Manual filter nodes. It's possible to not pass a group name value and the
>> resulting group will be anonymous. Are you suggesting to move this change to
>> the throttle filter patch?
>
>What is the use case?  Human users will stick to the legacy syntax
>because it's convenient.  Management tools will use the filter
>explicitly in the future, and it's easy for them to choose a name.
>
>Unless there is a need for this case I'd prefer to make the group name
>mandatory.  That way there are less code paths to worry about.

I think Kevin requested this though I don't really remember the use 
case.
>
>> > > @@ -87,32 +99,30 @@ ThrottleState *throttle_group_incref(const char *name)
>> > >
>> > >      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)) {
>> > > -            tg = iter;
>> > > -            break;
>> > > +    if (name) {
>> > > +        /* Look for an existing group with that name */
>> > > +        QTAILQ_FOREACH(iter, &throttle_groups, list) {
>> > > +            if (!g_strcmp0(name, iter->name)) {
>> > > +                tg = iter;
>> > > +                break;
>> > > +            }
>> > >          }
>> > >      }
>> > >
>> > >      /* Create a new one if not found */
>> > >      if (!tg) {
>> > > -        tg = g_new0(ThrottleGroup, 1);
>> > > +        /* new ThrottleGroup obj will have a refcnt = 1 */
>> > > +        tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
>> > >          tg->name = g_strdup(name);
>> > > -        tg->clock_type = QEMU_CLOCK_REALTIME;
>> > > -
>> > > -        if (qtest_enabled()) {
>> > > -            /* For testing block IO throttling only */
>> > > -            tg->clock_type = QEMU_CLOCK_VIRTUAL;
>> > > -        }
>> > > -        qemu_mutex_init(&tg->lock);
>> > > -        throttle_init(&tg->ts);
>> > > -        QLIST_INIT(&tg->head);
>> > > -
>> > > -        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
>> > > +        throttle_group_obj_complete((UserCreatable *)tg, &error_abort);
>> > >      }
>> > >
>> > > -    tg->refcount++;
>> > > +    qemu_mutex_lock(&tg->lock);
>> > > +    if (!QLIST_EMPTY(&tg->head)) {
>> > > +        /* only ref if the group is not empty */
>> > > +        object_ref(OBJECT(tg));
>> > > +    }
>> > > +    qemu_mutex_unlock(&tg->lock);
>> >
>> > How do the refcounts work in various cases (legacy -drive
>> > throttling.group and -object throttle-group with 0, 1, or multiple
>> > drives)?
>> >
>> > It's not obvious to me that this code works in all cases.
>>
>> Object is created with object_new(): ref count is 1
>> Each time we call throttle_group_incref() to add a new member to the group,
>> we increase the ref count by 1. We skip the first time we do that because
>> there's already a reference. When all members are removed, object is
>> deleted.
>
>If the ThrottleGroup was created with -object throttle-group it
>shouldn't disappear when the last member is unregistered because the QOM
>tree has 1 reference to the ThrottleGroup at all times due to
>user_creatable_add_type():
>
>  object_property_add_child(object_get_objects_root(),
>                            id, obj, &local_err);
>
>Is it okay to simplify the patch to:
>
>  if (tg) {
>      object_ref(OBJECT(tg));
>  } else {
>      tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
>      ...
>  }
>
>That would be clearer - in both legs of the if statement we take a
>reference to the object.  The if (!QLIST_EMPTY(tg->head)) check confuses
>me.

Ok, so tree objects (cli or QMP created) must be manually deleted then.  
Is this consistent with the management of other user creatable types?

>> > > +static void throttle_group_obj_init(Object *obj)
>> > > +{
>> > > +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> > > +
>> > > +    tg->clock_type = QEMU_CLOCK_REALTIME;
>> > > +    if (qtest_enabled()) {
>> > > +        /* For testing block IO throttling only */
>> > > +        tg->clock_type = QEMU_CLOCK_VIRTUAL;
>> > > +    }
>> > > +    tg->is_initialized = false;
>> > > +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
>> >
>> > Is the lock taken when the object-add QMP command or -object
>> > command-line argument are used?
>> >
>> > In any case, please do not assume that throttle_groups_lock is held for
>> > Object->init().  It should be possible to create new instances at any
>> > time.
>>
>> Hm, isn't the global lock held in both cases? And in the third case where we
>> create the object from throttle_group_incref() we hold the
>> throttle_groups_lock.
>
>At the moment I think throttle_groups_lock isn't strictly needed because
>incref/decref callers hold the QEMU global mutex anyway.
>
>But code accessing throttle_groups still has to be disciplined.  Since
>throttle_groups_lock exists, please use it consistently in all code
>paths.
>
>Alternatively you could remove the lock and document that
>throttle_groups is protected by the global mutex.  What we can't do is
>sometimes use throttle_groups_lock and sometimes not use it.

If we use throttle_groups_lock in throttle_group_obj_init() then we must 
give it up in throttle_group_incref() and retake it in 
throttle_group_obj_init(). Maybe indeed it's better to drop 
throttle_groups_lock altogether, since the ThrottleGroup refcounting 
always happens in a QMP or startup/cleanup context.
>
>> > > +static void throttle_group_set_limits(Object *obj, Visitor *v,
>> > > +                                      const char *name, void *opaque,
>> > > +                                      Error **errp)
>> > > +
>> > > +{
>> > > +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
>> > > +    ThrottleConfig cfg;
>> > > +    ThrottleLimits *arg = NULL;
>> > > +    Error *local_err = NULL;
>> > > +
>> > > +    arg = g_new0(ThrottleLimits, 1);
>> >
>> > Why does ThrottleLimits need to be allocated on the heap?
>>
>> It is passed to visit_type_ThrottleLimits which needs a double pointer.  The
>> alternative would be:
>>
>>    ThrottleLimits arg = { 0 }, *p = &arg;
>>    visit_type_ThrottleLimits(v, name, &p, &local_err);
>>
>> Which didn't seem very pretty at the time. Is it a problem?
>
>I was wondering because error code paths need to remember to free it.
>
>Looking more closely at qapi-visit.c I think it *must* be heap-allocated
>because qapi visit functions are allowed to fail and they free the
>object on failure.
>
>What you've done looks correct.



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

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-02 10:57         ` Manos Pitsidianakis
@ 2017-08-02 14:43           ` Stefan Hajnoczi
  2017-08-03  8:08           ` Kevin Wolf
  2017-08-08 13:01           ` Alberto Garcia
  2 siblings, 0 replies; 43+ messages in thread
From: Stefan Hajnoczi @ 2017-08-02 14:43 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel, Kevin Wolf, Alberto Garcia, qemu-block

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

On Wed, Aug 02, 2017 at 01:57:04PM +0300, Manos Pitsidianakis wrote:
> On Wed, Aug 02, 2017 at 11:39:22AM +0100, Stefan Hajnoczi wrote:
> > On Tue, Aug 01, 2017 at 07:49:33PM +0300, Manos Pitsidianakis wrote:
> > > On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
> > > > On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
> > > > > ThrottleGroup is converted to an object. This will allow the future
> > > > > throttle block filter drive easy creation and configuration of throttle
> > > > > groups in QMP and cli.
> > > > >
> > > > > A new QAPI struct, ThrottleLimits, is introduced to provide a shared
> > > > > struct for all throttle configuration needs in QMP.
> > > > >
> > > > > ThrottleGroups can be created via CLI as
> > > > >     -object throttle-group,id=foo,x-iops-total=100,x-..
> > > > > where x-* are individual limit properties. Since we can't add non-scalar
> > > > > properties in -object this interface must be used instead. However,
> > > > > setting these properties must be disabled after initialization because
> > > > > certain combinations of limits are forbidden and thus configuration
> > > > > changes should be done in one transaction. The individual properties
> > > > > will go away when support for non-scalar values in CLI is implemented
> > > > > and thus are marked as experimental.
> > > > >
> > > > > ThrottleGroup also has a `limits` property that uses the ThrottleLimits
> > > > > struct.  It can be used to create ThrottleGroups or set the
> > > > > configuration in existing groups as follows:
> > > > >
> > > > > { "execute": "object-add",
> > > > >   "arguments": {
> > > > >     "qom-type": "throttle-group",
> > > > >     "id": "foo",
> > > > >     "props" : {
> > > > >       "limits": {
> > > > >           "iops-total": 100
> > > > >       }
> > > > >     }
> > > > >   }
> > > > > }
> > > > > { "execute" : "qom-set",
> > > > >     "arguments" : {
> > > > >         "path" : "foo",
> > > > >         "property" : "limits",
> > > > >         "value" : {
> > > > >             "iops-total" : 99
> > > > >         }
> > > > >     }
> > > > > }
> > > > >
> > > > > This also means a group's configuration can be fetched with qom-get.
> > > > >
> > > > > ThrottleGroups can be anonymous which means they can't get accessed by
> > > > > other users ie they will always be units instead of group (Because they
> > > > > have one ThrottleGroupMember).
> > > >
> > > > blockdev.c automatically assigns -drive id= to the group name if
> > > > throttling.group= wasn't given.  So who will use anonymous single-drive
> > > > mode?
> > > 
> > > Manual filter nodes. It's possible to not pass a group name value and the
> > > resulting group will be anonymous. Are you suggesting to move this change to
> > > the throttle filter patch?
> > 
> > What is the use case?  Human users will stick to the legacy syntax
> > because it's convenient.  Management tools will use the filter
> > explicitly in the future, and it's easy for them to choose a name.
> > 
> > Unless there is a need for this case I'd prefer to make the group name
> > mandatory.  That way there are less code paths to worry about.
> 
> I think Kevin requested this though I don't really remember the use case.

Kevin: Do you still want this?

> > 
> > > > > @@ -87,32 +99,30 @@ ThrottleState *throttle_group_incref(const char *name)
> > > > >
> > > > >      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)) {
> > > > > -            tg = iter;
> > > > > -            break;
> > > > > +    if (name) {
> > > > > +        /* Look for an existing group with that name */
> > > > > +        QTAILQ_FOREACH(iter, &throttle_groups, list) {
> > > > > +            if (!g_strcmp0(name, iter->name)) {
> > > > > +                tg = iter;
> > > > > +                break;
> > > > > +            }
> > > > >          }
> > > > >      }
> > > > >
> > > > >      /* Create a new one if not found */
> > > > >      if (!tg) {
> > > > > -        tg = g_new0(ThrottleGroup, 1);
> > > > > +        /* new ThrottleGroup obj will have a refcnt = 1 */
> > > > > +        tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
> > > > >          tg->name = g_strdup(name);
> > > > > -        tg->clock_type = QEMU_CLOCK_REALTIME;
> > > > > -
> > > > > -        if (qtest_enabled()) {
> > > > > -            /* For testing block IO throttling only */
> > > > > -            tg->clock_type = QEMU_CLOCK_VIRTUAL;
> > > > > -        }
> > > > > -        qemu_mutex_init(&tg->lock);
> > > > > -        throttle_init(&tg->ts);
> > > > > -        QLIST_INIT(&tg->head);
> > > > > -
> > > > > -        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> > > > > +        throttle_group_obj_complete((UserCreatable *)tg, &error_abort);
> > > > >      }
> > > > >
> > > > > -    tg->refcount++;
> > > > > +    qemu_mutex_lock(&tg->lock);
> > > > > +    if (!QLIST_EMPTY(&tg->head)) {
> > > > > +        /* only ref if the group is not empty */
> > > > > +        object_ref(OBJECT(tg));
> > > > > +    }
> > > > > +    qemu_mutex_unlock(&tg->lock);
> > > >
> > > > How do the refcounts work in various cases (legacy -drive
> > > > throttling.group and -object throttle-group with 0, 1, or multiple
> > > > drives)?
> > > >
> > > > It's not obvious to me that this code works in all cases.
> > > 
> > > Object is created with object_new(): ref count is 1
> > > Each time we call throttle_group_incref() to add a new member to the group,
> > > we increase the ref count by 1. We skip the first time we do that because
> > > there's already a reference. When all members are removed, object is
> > > deleted.
> > 
> > If the ThrottleGroup was created with -object throttle-group it
> > shouldn't disappear when the last member is unregistered because the QOM
> > tree has 1 reference to the ThrottleGroup at all times due to
> > user_creatable_add_type():
> > 
> >  object_property_add_child(object_get_objects_root(),
> >                            id, obj, &local_err);
> > 
> > Is it okay to simplify the patch to:
> > 
> >  if (tg) {
> >      object_ref(OBJECT(tg));
> >  } else {
> >      tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
> >      ...
> >  }
> > 
> > That would be clearer - in both legs of the if statement we take a
> > reference to the object.  The if (!QLIST_EMPTY(tg->head)) check confuses
> > me.
> 
> Ok, so tree objects (cli or QMP created) must be manually deleted then.  Is
> this consistent with the management of other user creatable types?

Yes, the object-del command must be used to delete user created objects.

> > > > > +static void throttle_group_obj_init(Object *obj)
> > > > > +{
> > > > > +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> > > > > +
> > > > > +    tg->clock_type = QEMU_CLOCK_REALTIME;
> > > > > +    if (qtest_enabled()) {
> > > > > +        /* For testing block IO throttling only */
> > > > > +        tg->clock_type = QEMU_CLOCK_VIRTUAL;
> > > > > +    }
> > > > > +    tg->is_initialized = false;
> > > > > +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> > > >
> > > > Is the lock taken when the object-add QMP command or -object
> > > > command-line argument are used?
> > > >
> > > > In any case, please do not assume that throttle_groups_lock is held for
> > > > Object->init().  It should be possible to create new instances at any
> > > > time.
> > > 
> > > Hm, isn't the global lock held in both cases? And in the third case where we
> > > create the object from throttle_group_incref() we hold the
> > > throttle_groups_lock.
> > 
> > At the moment I think throttle_groups_lock isn't strictly needed because
> > incref/decref callers hold the QEMU global mutex anyway.
> > 
> > But code accessing throttle_groups still has to be disciplined.  Since
> > throttle_groups_lock exists, please use it consistently in all code
> > paths.
> > 
> > Alternatively you could remove the lock and document that
> > throttle_groups is protected by the global mutex.  What we can't do is
> > sometimes use throttle_groups_lock and sometimes not use it.
> 
> If we use throttle_groups_lock in throttle_group_obj_init() then we must
> give it up in throttle_group_incref() and retake it in
> throttle_group_obj_init(). Maybe indeed it's better to drop
> throttle_groups_lock altogether, since the ThrottleGroup refcounting always
> happens in a QMP or startup/cleanup context.

We can use just the QEMU global mutex.  Please mark up the doc comments
of the functions that must be called under the global mutex so this
assumption is clear.

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver Manos Pitsidianakis
  2017-08-01 16:14   ` Stefan Hajnoczi
@ 2017-08-03  8:07   ` Kevin Wolf
  2017-08-03 11:48     ` Manos Pitsidianakis
  2017-08-03 11:58     ` Eric Blake
  2017-08-08 13:13   ` Alberto Garcia
  2 siblings, 2 replies; 43+ messages in thread
From: Kevin Wolf @ 2017-08-03  8:07 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, Stefan Hajnoczi, Alberto Garcia, qemu-block

Am 31.07.2017 um 11:54 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 syntax
> -drive driver=throttle,file.filename=foo.qcow2, \
>         limits.iops-total=...,throttle-group=bar
> 
> The configuration flags and their semantics are identical to the
> hardcoded throttling ones.
> 
> A node can be created referring to an existing group, and will overwrite
> its limits if any are specified, otherwise they are retained.
> 
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> ---
>  block/Makefile.objs             |   1 +
>  block/throttle.c                | 395 ++++++++++++++++++++++++++++++++++++++++
>  include/qemu/throttle-options.h |   1 +
>  3 files changed, 397 insertions(+)
>  create mode 100644 block/throttle.c
> 
> diff --git a/block/Makefile.objs b/block/Makefile.objs
> index 2aaede4ae1..6eaf78a046 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..f3395462fb
> --- /dev/null
> +++ b/block/throttle.c
> @@ -0,0 +1,395 @@
> +/*
> + * 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"
> +
> +#undef THROTTLE_OPT_PREFIX
> +#define THROTTLE_OPT_PREFIX "limits."
> +static QemuOptsList throttle_opts = {
> +    .name = "throttle",
> +    .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
> +    .desc = {
> +        THROTTLE_OPTS,
> +        {
> +            .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 int throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg,
> +                                     Error **errp)
> +{
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL)) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
> +                                0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ)) {
> +        cfg->buckets[THROTTLE_BPS_READ].avg  =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
> +                                0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE)) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].avg =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
> +                                0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL)) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
> +                                0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ)) {
> +        cfg->buckets[THROTTLE_OPS_READ].avg =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
> +                                0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE)) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].avg =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
> +                                0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX)) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].max =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_TOTAL_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX)) {
> +        cfg->buckets[THROTTLE_BPS_READ].max  =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_READ_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX)) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].max =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_WRITE_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX)) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].max =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_TOTAL_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX)) {
> +        cfg->buckets[THROTTLE_OPS_READ].max =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_READ_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX)) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].max =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_WRITE_MAX, 0);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
> +            error_setg(errp, "%s value must be in the range [0, %u]",
> +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
> +                       UINT_MAX);
> +            return -1;
> +        }
> +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH)) {
> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
> +            error_setg(errp, "%s must be in the range [0, %u]",
> +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
> +                       UINT_MAX);
> +            return -1;
> +        }
> +        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
> +            error_setg(errp, "%s must be in the range [0, %u]",
> +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
> +                       UINT_MAX);
> +            return -1;
> +        }
> +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
> +            error_setg(errp, "%s must be in the range [0, %u]",
> +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
> +                       UINT_MAX);
> +            return -1;
> +        }
> +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
> +            error_setg(errp, "%s must be in the range [0, %u]",
> +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
> +                       UINT_MAX);
> +            return -1;
> +        }
> +        cfg->buckets[THROTTLE_OPS_READ].burst_length =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
> +            error_setg(errp, "%s must be in the range [0, %u]",
> +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
> +                       UINT_MAX);
> +            return -1;
> +        }
> +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> +                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
> +    }
> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE)) {
> +        cfg->op_size =
> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
> +                                0);
> +    }
> +    return 0;
> +}

This function is very repetitive, but each block is long enough that
you have to look closely to review whether the right constants are used
everywhere.

Maybe this could become a bit more readable with a macro or two?

> +static int throttle_configure_tgm(BlockDriverState *bs,
> +                                  ThrottleGroupMember *tgm,
> +                                  QDict *options, Error **errp)
> +{
> +    int ret;
> +    ThrottleConfig cfg;
> +    const char *group_name = NULL;
> +    Error *local_err = NULL;
> +    QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return -EINVAL;
> +    }

As Stefan said, qemu_opts_create() can't fail if you pass NULL for id,
so you can just use &error_abort.

> +
> +    qemu_opts_absorb_qdict(opts, options, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        goto err;
> +    }
> +
> +    /* If no name is specified, an anonymous group will be created */
> +    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
> +
> +    /* Register membership to group with name group_name */
> +    throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));

The documentation of throttle_group_register_tgm() suggests that you
have to pass a string here, but group_name can be NULL if the option
wasn't given. Probably just means that the comment for that function
needs to be updated.

> +    /* Copy previous configuration */
> +    throttle_group_get_config(tgm, &cfg);
> +
> +    /* Change limits if user has specified them */
> +    if (throttle_extract_options(opts, &cfg, errp) ||
> +        !throttle_is_valid(&cfg, errp)) {
> +        throttle_group_unregister_tgm(tgm);
> +        goto err;
> +    }
> +    /* Update group configuration */
> +    throttle_group_config(tgm, &cfg);
> +
> +    ret = 0;
> +    goto fin;
> +
> +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;
> +
> +    bs->file = bdrv_open_child(NULL, options, "file",
> +                                    bs, &child_file, false, errp);

Indentation is off.

> +    if (!bs->file) {
> +        return -EINVAL;
> +    }
> +
> +    return throttle_configure_tgm(bs, tgm, options, errp);
> +}
> +
> +static void throttle_close(BlockDriverState *bs)
> +{
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    throttle_group_unregister_tgm(tgm);
> +}
> +
> +
> +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);
> +}

I think we want to set BlockDriver.supported_write_flags so that passing
down flags is actually of any use.

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

The same is true for BlockDriver.supported_zero_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_group_detach_aio_context(tgm);
> +}
> +
> +static void throttle_attach_aio_context(BlockDriverState *bs,
> +                                    AioContext *new_context)

Indentation is off here...

> +{
> +    ThrottleGroupMember *tgm = bs->opaque;
> +    throttle_group_attach_aio_context(tgm, new_context);
> +}
> +
> +static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
> +                                      BlockReopenQueue *queue, Error **errp)

...and here.

> +{
> +    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;
> +
> +    throttle_group_unregister_tgm(tgm);
> +    g_free(state->bs->opaque);
> +    state->bs->opaque = state->opaque;
> +    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 bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
> +                                                 BlockDriverState *candidate)
> +{
> +    return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
> +}
> +
> +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   =   throttle_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,
> +};

What about .bdrv_co_get_block_status?

> +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 182b7896e1..3528a8f4a2 100644
> --- a/include/qemu/throttle-options.h
> +++ b/include/qemu/throttle-options.h
> @@ -29,6 +29,7 @@
>  #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 "throttle-group"
>  
>  #define THROTTLE_OPT_PREFIX "throttling."
>  #define THROTTLE_OPTS \

Kevin

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

* Re: [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests Manos Pitsidianakis
@ 2017-08-03  8:07   ` Kevin Wolf
  2017-08-03 13:24     ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Kevin Wolf @ 2017-08-03  8:07 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, Stefan Hajnoczi, Alberto Garcia, qemu-block

Am 31.07.2017 um 11:54 hat Manos Pitsidianakis geschrieben:
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

I would add at least two more cases:

* Both limits and throttle-group are given in blockdev-add
* limits and throttle-group are both missing

It would also be nice to test that query-block reflects the new throttle
group limits correctly when they are changed after the fact.

Kevin

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-02 10:57         ` Manos Pitsidianakis
  2017-08-02 14:43           ` Stefan Hajnoczi
@ 2017-08-03  8:08           ` Kevin Wolf
  2017-08-03 10:53             ` Stefan Hajnoczi
  2017-08-08 13:01           ` Alberto Garcia
  2 siblings, 1 reply; 43+ messages in thread
From: Kevin Wolf @ 2017-08-03  8:08 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi, qemu-devel, Alberto Garcia,
	qemu-block

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

Am 02.08.2017 um 12:57 hat Manos Pitsidianakis geschrieben:
> On Wed, Aug 02, 2017 at 11:39:22AM +0100, Stefan Hajnoczi wrote:
> > On Tue, Aug 01, 2017 at 07:49:33PM +0300, Manos Pitsidianakis wrote:
> > > On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
> > > > On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
> > > > > ThrottleGroup is converted to an object. This will allow the future
> > > > > throttle block filter drive easy creation and configuration of throttle
> > > > > groups in QMP and cli.
> > > > >
> > > > > A new QAPI struct, ThrottleLimits, is introduced to provide a shared
> > > > > struct for all throttle configuration needs in QMP.
> > > > >
> > > > > ThrottleGroups can be created via CLI as
> > > > >     -object throttle-group,id=foo,x-iops-total=100,x-..
> > > > > where x-* are individual limit properties. Since we can't add non-scalar
> > > > > properties in -object this interface must be used instead. However,
> > > > > setting these properties must be disabled after initialization because
> > > > > certain combinations of limits are forbidden and thus configuration
> > > > > changes should be done in one transaction. The individual properties
> > > > > will go away when support for non-scalar values in CLI is implemented
> > > > > and thus are marked as experimental.
> > > > >
> > > > > ThrottleGroup also has a `limits` property that uses the ThrottleLimits
> > > > > struct.  It can be used to create ThrottleGroups or set the
> > > > > configuration in existing groups as follows:
> > > > >
> > > > > { "execute": "object-add",
> > > > >   "arguments": {
> > > > >     "qom-type": "throttle-group",
> > > > >     "id": "foo",
> > > > >     "props" : {
> > > > >       "limits": {
> > > > >           "iops-total": 100
> > > > >       }
> > > > >     }
> > > > >   }
> > > > > }
> > > > > { "execute" : "qom-set",
> > > > >     "arguments" : {
> > > > >         "path" : "foo",
> > > > >         "property" : "limits",
> > > > >         "value" : {
> > > > >             "iops-total" : 99
> > > > >         }
> > > > >     }
> > > > > }
> > > > >
> > > > > This also means a group's configuration can be fetched with qom-get.
> > > > >
> > > > > ThrottleGroups can be anonymous which means they can't get accessed by
> > > > > other users ie they will always be units instead of group (Because they
> > > > > have one ThrottleGroupMember).
> > > >
> > > > blockdev.c automatically assigns -drive id= to the group name if
> > > > throttling.group= wasn't given.  So who will use anonymous single-drive
> > > > mode?
> > > 
> > > Manual filter nodes. It's possible to not pass a group name value and the
> > > resulting group will be anonymous. Are you suggesting to move this change to
> > > the throttle filter patch?
> > 
> > What is the use case?  Human users will stick to the legacy syntax
> > because it's convenient.  Management tools will use the filter
> > explicitly in the future, and it's easy for them to choose a name.
> > 
> > Unless there is a need for this case I'd prefer to make the group name
> > mandatory.  That way there are less code paths to worry about.
> 
> I think Kevin requested this though I don't really remember the use case.

There is no legacy syntax for putting a throttle node anywhere but at
the root of a BlockBackend. If you want to throttle e.g. only a specific
backing file, you need to manually create a throttle node.

(We tend to forget this occasionally, but the work you're doing is not
only cleanup just for fun, but it's actually new features that enable
new use cases by making everything more flexible.)

Kevin

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

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-03  8:08           ` Kevin Wolf
@ 2017-08-03 10:53             ` Stefan Hajnoczi
  2017-08-03 11:17               ` Kevin Wolf
  0 siblings, 1 reply; 43+ messages in thread
From: Stefan Hajnoczi @ 2017-08-03 10:53 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: Manos Pitsidianakis, qemu-devel, Alberto Garcia, qemu-block

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

On Thu, Aug 03, 2017 at 10:08:01AM +0200, Kevin Wolf wrote:
> Am 02.08.2017 um 12:57 hat Manos Pitsidianakis geschrieben:
> > On Wed, Aug 02, 2017 at 11:39:22AM +0100, Stefan Hajnoczi wrote:
> > > On Tue, Aug 01, 2017 at 07:49:33PM +0300, Manos Pitsidianakis wrote:
> > > > On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
> > > > > On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
> > > > > > ThrottleGroup is converted to an object. This will allow the future
> > > > > > throttle block filter drive easy creation and configuration of throttle
> > > > > > groups in QMP and cli.
> > > > > >
> > > > > > A new QAPI struct, ThrottleLimits, is introduced to provide a shared
> > > > > > struct for all throttle configuration needs in QMP.
> > > > > >
> > > > > > ThrottleGroups can be created via CLI as
> > > > > >     -object throttle-group,id=foo,x-iops-total=100,x-..
> > > > > > where x-* are individual limit properties. Since we can't add non-scalar
> > > > > > properties in -object this interface must be used instead. However,
> > > > > > setting these properties must be disabled after initialization because
> > > > > > certain combinations of limits are forbidden and thus configuration
> > > > > > changes should be done in one transaction. The individual properties
> > > > > > will go away when support for non-scalar values in CLI is implemented
> > > > > > and thus are marked as experimental.
> > > > > >
> > > > > > ThrottleGroup also has a `limits` property that uses the ThrottleLimits
> > > > > > struct.  It can be used to create ThrottleGroups or set the
> > > > > > configuration in existing groups as follows:
> > > > > >
> > > > > > { "execute": "object-add",
> > > > > >   "arguments": {
> > > > > >     "qom-type": "throttle-group",
> > > > > >     "id": "foo",
> > > > > >     "props" : {
> > > > > >       "limits": {
> > > > > >           "iops-total": 100
> > > > > >       }
> > > > > >     }
> > > > > >   }
> > > > > > }
> > > > > > { "execute" : "qom-set",
> > > > > >     "arguments" : {
> > > > > >         "path" : "foo",
> > > > > >         "property" : "limits",
> > > > > >         "value" : {
> > > > > >             "iops-total" : 99
> > > > > >         }
> > > > > >     }
> > > > > > }
> > > > > >
> > > > > > This also means a group's configuration can be fetched with qom-get.
> > > > > >
> > > > > > ThrottleGroups can be anonymous which means they can't get accessed by
> > > > > > other users ie they will always be units instead of group (Because they
> > > > > > have one ThrottleGroupMember).
> > > > >
> > > > > blockdev.c automatically assigns -drive id= to the group name if
> > > > > throttling.group= wasn't given.  So who will use anonymous single-drive
> > > > > mode?
> > > > 
> > > > Manual filter nodes. It's possible to not pass a group name value and the
> > > > resulting group will be anonymous. Are you suggesting to move this change to
> > > > the throttle filter patch?
> > > 
> > > What is the use case?  Human users will stick to the legacy syntax
> > > because it's convenient.  Management tools will use the filter
> > > explicitly in the future, and it's easy for them to choose a name.
> > > 
> > > Unless there is a need for this case I'd prefer to make the group name
> > > mandatory.  That way there are less code paths to worry about.
> > 
> > I think Kevin requested this though I don't really remember the use case.
> 
> There is no legacy syntax for putting a throttle node anywhere but at
> the root of a BlockBackend. If you want to throttle e.g. only a specific
> backing file, you need to manually create a throttle node.
> 
> (We tend to forget this occasionally, but the work you're doing is not
> only cleanup just for fun, but it's actually new features that enable
> new use cases by making everything more flexible.)

It's not clear to me from your reply whether you support anonymous
throttle groups or not.  It is possible to throttle arbitrary nodes in
the graph either way.

Stefan

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

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-03 10:53             ` Stefan Hajnoczi
@ 2017-08-03 11:17               ` Kevin Wolf
  2017-08-03 12:29                 ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Kevin Wolf @ 2017-08-03 11:17 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Manos Pitsidianakis, qemu-devel, Alberto Garcia, qemu-block

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

Am 03.08.2017 um 12:53 hat Stefan Hajnoczi geschrieben:
> On Thu, Aug 03, 2017 at 10:08:01AM +0200, Kevin Wolf wrote:
> > Am 02.08.2017 um 12:57 hat Manos Pitsidianakis geschrieben:
> > > On Wed, Aug 02, 2017 at 11:39:22AM +0100, Stefan Hajnoczi wrote:
> > > > On Tue, Aug 01, 2017 at 07:49:33PM +0300, Manos Pitsidianakis wrote:
> > > > > On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
> > > > > > On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
> > > > > > > ThrottleGroup is converted to an object. This will allow the future
> > > > > > > throttle block filter drive easy creation and configuration of throttle
> > > > > > > groups in QMP and cli.
> > > > > > >
> > > > > > > A new QAPI struct, ThrottleLimits, is introduced to provide a shared
> > > > > > > struct for all throttle configuration needs in QMP.
> > > > > > >
> > > > > > > ThrottleGroups can be created via CLI as
> > > > > > >     -object throttle-group,id=foo,x-iops-total=100,x-..
> > > > > > > where x-* are individual limit properties. Since we can't add non-scalar
> > > > > > > properties in -object this interface must be used instead. However,
> > > > > > > setting these properties must be disabled after initialization because
> > > > > > > certain combinations of limits are forbidden and thus configuration
> > > > > > > changes should be done in one transaction. The individual properties
> > > > > > > will go away when support for non-scalar values in CLI is implemented
> > > > > > > and thus are marked as experimental.
> > > > > > >
> > > > > > > ThrottleGroup also has a `limits` property that uses the ThrottleLimits
> > > > > > > struct.  It can be used to create ThrottleGroups or set the
> > > > > > > configuration in existing groups as follows:
> > > > > > >
> > > > > > > { "execute": "object-add",
> > > > > > >   "arguments": {
> > > > > > >     "qom-type": "throttle-group",
> > > > > > >     "id": "foo",
> > > > > > >     "props" : {
> > > > > > >       "limits": {
> > > > > > >           "iops-total": 100
> > > > > > >       }
> > > > > > >     }
> > > > > > >   }
> > > > > > > }
> > > > > > > { "execute" : "qom-set",
> > > > > > >     "arguments" : {
> > > > > > >         "path" : "foo",
> > > > > > >         "property" : "limits",
> > > > > > >         "value" : {
> > > > > > >             "iops-total" : 99
> > > > > > >         }
> > > > > > >     }
> > > > > > > }
> > > > > > >
> > > > > > > This also means a group's configuration can be fetched with qom-get.
> > > > > > >
> > > > > > > ThrottleGroups can be anonymous which means they can't get accessed by
> > > > > > > other users ie they will always be units instead of group (Because they
> > > > > > > have one ThrottleGroupMember).
> > > > > >
> > > > > > blockdev.c automatically assigns -drive id= to the group name if
> > > > > > throttling.group= wasn't given.  So who will use anonymous single-drive
> > > > > > mode?
> > > > > 
> > > > > Manual filter nodes. It's possible to not pass a group name value and the
> > > > > resulting group will be anonymous. Are you suggesting to move this change to
> > > > > the throttle filter patch?
> > > > 
> > > > What is the use case?  Human users will stick to the legacy syntax
> > > > because it's convenient.  Management tools will use the filter
> > > > explicitly in the future, and it's easy for them to choose a name.
> > > > 
> > > > Unless there is a need for this case I'd prefer to make the group name
> > > > mandatory.  That way there are less code paths to worry about.
> > > 
> > > I think Kevin requested this though I don't really remember the use case.
> > 
> > There is no legacy syntax for putting a throttle node anywhere but at
> > the root of a BlockBackend. If you want to throttle e.g. only a specific
> > backing file, you need to manually create a throttle node.
> > 
> > (We tend to forget this occasionally, but the work you're doing is not
> > only cleanup just for fun, but it's actually new features that enable
> > new use cases by making everything more flexible.)
> 
> It's not clear to me from your reply whether you support anonymous
> throttle groups or not.  It is possible to throttle arbitrary nodes in
> the graph either way.

I think it would be nice for human users to have them, but on second
thought you're right that it's just syntactic sugar for an explicit
-object, so if you think there is any difficulty with supporting
anonymous groups, feel free to drop them.

Hm, actually... Does this mean that then the whole 'limits' option in
the throttle driver could go away, with all of the parsing code, and the
group name becomes mandatory? That certainly does look tempting.

Kevin

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-03  8:07   ` Kevin Wolf
@ 2017-08-03 11:48     ` Manos Pitsidianakis
  2017-08-03 12:05       ` Kevin Wolf
  2017-08-03 11:58     ` Eric Blake
  1 sibling, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-03 11:48 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, Stefan Hajnoczi, Alberto Garcia, qemu-block

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

On Thu, Aug 03, 2017 at 10:07:41AM +0200, Kevin Wolf wrote:
>Am 31.07.2017 um 11:54 hat Manos Pitsidianakis geschrieben:
>> +/* Extract ThrottleConfig options. Assumes cfg is initialized and will be
>> + * checked for validity.
>> + */
>> +static int throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg,
>> +                                     Error **errp)
>> +{
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL)) {
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
>> +                                0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ)) {
>> +        cfg->buckets[THROTTLE_BPS_READ].avg  =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
>> +                                0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE)) {
>> +        cfg->buckets[THROTTLE_BPS_WRITE].avg =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
>> +                                0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL)) {
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
>> +                                0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ)) {
>> +        cfg->buckets[THROTTLE_OPS_READ].avg =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
>> +                                0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE)) {
>> +        cfg->buckets[THROTTLE_OPS_WRITE].avg =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
>> +                                0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX)) {
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].max =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_TOTAL_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX)) {
>> +        cfg->buckets[THROTTLE_BPS_READ].max  =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_READ_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX)) {
>> +        cfg->buckets[THROTTLE_BPS_WRITE].max =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_WRITE_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX)) {
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].max =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_TOTAL_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX)) {
>> +        cfg->buckets[THROTTLE_OPS_READ].max =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_READ_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX)) {
>> +        cfg->buckets[THROTTLE_OPS_WRITE].max =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_WRITE_MAX, 0);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
>> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
>> +            error_setg(errp, "%s value must be in the range [0, %u]",
>> +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
>> +                       UINT_MAX);
>> +            return -1;
>> +        }
>> +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH)) {
>> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
>> +            error_setg(errp, "%s must be in the range [0, %u]",
>> +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
>> +                       UINT_MAX);
>> +            return -1;
>> +        }
>> +        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
>> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
>> +            error_setg(errp, "%s must be in the range [0, %u]",
>> +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
>> +                       UINT_MAX);
>> +            return -1;
>> +        }
>> +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
>> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
>> +            error_setg(errp, "%s must be in the range [0, %u]",
>> +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
>> +                       UINT_MAX);
>> +            return -1;
>> +        }
>> +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
>> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
>> +            error_setg(errp, "%s must be in the range [0, %u]",
>> +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
>> +                       UINT_MAX);
>> +            return -1;
>> +        }
>> +        cfg->buckets[THROTTLE_OPS_READ].burst_length =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
>> +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
>> +            error_setg(errp, "%s must be in the range [0, %u]",
>> +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
>> +                       UINT_MAX);
>> +            return -1;
>> +        }
>> +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
>> +                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
>> +    }
>> +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE)) {
>> +        cfg->op_size =
>> +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
>> +                                0);
>> +    }
>> +    return 0;
>> +}
>
>This function is very repetitive, but each block is long enough that
>you have to look closely to review whether the right constants are used
>everywhere.
>
>Maybe this could become a bit more readable with a macro or two?

How about this?


#   #define IF_OPT_SET(rvalue,opt_name) \
#       if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX opt_name)) { \
#           rvalue = qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX opt_name, 0); } 

#       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_TOTAL].avg, QEMU_OPT_BPS_TOTAL);
#       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_READ].avg, QEMU_OPT_BPS_READ);
#       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_WRITE].avg, QEMU_OPT_BPS_WRITE);
#       IF_OPT_SET(cfg->buckets[THROTTLE_OPS_TOTAL].avg, QEMU_OPT_IOPS_TOTAL);
#       IF_OPT_SET(cfg->buckets[THROTTLE_OPS_READ].avg, QEMU_OPT_IOPS_READ);
#       IF_OPT_SET(cfg->buckets[THROTTLE_OPS_WRITE].avg, QEMU_OPT_IOPS_WRITE);
#       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_TOTAL].max, QEMU_OPT_BPS_TOTAL_MAX);
#       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_READ].max , QEMU_OPT_BPS_READ_MAX);
#       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_WRITE].max, QEMU_OPT_BPS_WRITE_MAX);
#       IF_OPT_SET(cfg->buckets[THROTTLE_OPS_TOTAL].max, QEMU_OPT_IOPS_TOTAL_MAX);
#       IF_OPT_SET(cfg->buckets[THROTTLE_OPS_READ].max, QEMU_OPT_IOPS_READ_MAX);
#       IF_OPT_SET(cfg->buckets[THROTTLE_OPS_WRITE].max, QEMU_OPT_IOPS_WRITE_MAX);
#       IF_OPT_SET(cfg->op_size, QEMU_OPT_IOPS_SIZE);

#   #define IF_OPT_UINT_SET(rvalue,opt_name) \
#       if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX opt_name)) { \
#           if (qemu_opt_get_number(opts,  \
#                       THROTTLE_OPT_PREFIX opt_name, 1) > UINT_MAX) { \
#               error_setg(errp, "%s value must be in the range [0, %u]", \
#                          THROTTLE_OPT_PREFIX opt_name, UINT_MAX); \
#               return -1; \
#           } \
#           rvalue = qemu_opt_get_number(opts, opt_name, 1); \
#       }

#       IF_OPT_UINT_SET(cfg->buckets[THROTTLE_BPS_TOTAL].burst_length,
#                       QEMU_OPT_BPS_TOTAL_MAX_LENGTH);
#       IF_OPT_UINT_SET(cfg->buckets[THROTTLE_BPS_READ].burst_length,
#                       QEMU_OPT_BPS_READ_MAX_LENGTH);
#       IF_OPT_UINT_SET(cfg->buckets[THROTTLE_BPS_WRITE].burst_length,
#                       QEMU_OPT_BPS_WRITE_MAX_LENGTH);
#       IF_OPT_UINT_SET(cfg->buckets[THROTTLE_OPS_TOTAL].burst_length,
#                       QEMU_OPT_IOPS_TOTAL_MAX_LENGTH);
#       IF_OPT_UINT_SET(cfg->buckets[THROTTLE_OPS_READ].burst_length,
#                       QEMU_OPT_IOPS_READ_MAX_LENGTH);
#       IF_OPT_UINT_SET(cfg->buckets[THROTTLE_OPS_WRITE].burst_length,
#                       QEMU_OPT_IOPS_WRITE_MAX_LENGTH);



>> +static int throttle_configure_tgm(BlockDriverState *bs,
>> +                                  ThrottleGroupMember *tgm,
>> +                                  QDict *options, Error **errp)
>> +{
>> +    int ret;
>> +    ThrottleConfig cfg;
>> +    const char *group_name = NULL;
>> +    Error *local_err = NULL;
>> +    QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &local_err);
>> +    if (local_err) {
>> +        error_propagate(errp, local_err);
>> +        return -EINVAL;
>> +    }
>
>As Stefan said, qemu_opts_create() can't fail if you pass NULL for id,
>so you can just use &error_abort.
>
>> +
>> +    qemu_opts_absorb_qdict(opts, options, &local_err);
>> +    if (local_err) {
>> +        error_propagate(errp, local_err);
>> +        goto err;
>> +    }
>> +
>> +    /* If no name is specified, an anonymous group will be created */
>> +    group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
>> +
>> +    /* Register membership to group with name group_name */
>> +    throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
>
>The documentation of throttle_group_register_tgm() suggests that you
>have to pass a string here, but group_name can be NULL if the option
>wasn't given. Probably just means that the comment for that function
>needs to be updated.
>
>> +    /* Copy previous configuration */
>> +    throttle_group_get_config(tgm, &cfg);
>> +
>> +    /* Change limits if user has specified them */
>> +    if (throttle_extract_options(opts, &cfg, errp) ||
>> +        !throttle_is_valid(&cfg, errp)) {
>> +        throttle_group_unregister_tgm(tgm);
>> +        goto err;
>> +    }
>> +    /* Update group configuration */
>> +    throttle_group_config(tgm, &cfg);
>> +
>> +    ret = 0;
>> +    goto fin;
>> +
>> +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;
>> +
>> +    bs->file = bdrv_open_child(NULL, options, "file",
>> +                                    bs, &child_file, false, errp);
>
>Indentation is off.
>
>> +    if (!bs->file) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    return throttle_configure_tgm(bs, tgm, options, errp);
>> +}
>> +
>> +static void throttle_close(BlockDriverState *bs)
>> +{
>> +    ThrottleGroupMember *tgm = bs->opaque;
>> +    throttle_group_unregister_tgm(tgm);
>> +}
>> +
>> +
>> +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);
>> +}
>
>I think we want to set BlockDriver.supported_write_flags so that passing
>down flags is actually of any use.

Can you explain what you mean? Do you mean doing this in 
throttle_open():

    bs->supported_write_flags = bs->file->bs->supported_write_flags;
    bs->supported_zero_flags = bs->file->bs->supported_zero_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);
>> +}
>
>The same is true for BlockDriver.supported_zero_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_group_detach_aio_context(tgm);
>> +}
>> +
>> +static void throttle_attach_aio_context(BlockDriverState *bs,
>> +                                    AioContext *new_context)
>
>Indentation is off here...
>
>> +{
>> +    ThrottleGroupMember *tgm = bs->opaque;
>> +    throttle_group_attach_aio_context(tgm, new_context);
>> +}
>> +
>> +static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
>> +                                      BlockReopenQueue *queue, Error **errp)
>
>...and here.
>
>> +{
>> +    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;
>> +
>> +    throttle_group_unregister_tgm(tgm);
>> +    g_free(state->bs->opaque);
>> +    state->bs->opaque = state->opaque;
>> +    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 bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
>> +                                                 BlockDriverState *candidate)
>> +{
>> +    return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
>> +}
>> +
>> +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   =   throttle_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,
>> +};
>
>What about .bdrv_co_get_block_status?

We should use the default implementation here that you merged into your 
2.11 tree, thanks for noticing and for all the comments.
>
>> +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 182b7896e1..3528a8f4a2 100644
>> --- a/include/qemu/throttle-options.h
>> +++ b/include/qemu/throttle-options.h
>> @@ -29,6 +29,7 @@
>>  #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 "throttle-group"
>>
>>  #define THROTTLE_OPT_PREFIX "throttling."
>>  #define THROTTLE_OPTS \
>
>Kevin
>

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-03  8:07   ` Kevin Wolf
  2017-08-03 11:48     ` Manos Pitsidianakis
@ 2017-08-03 11:58     ` Eric Blake
  2017-08-03 13:56       ` Manos Pitsidianakis
  1 sibling, 1 reply; 43+ messages in thread
From: Eric Blake @ 2017-08-03 11:58 UTC (permalink / raw)
  To: Kevin Wolf, Manos Pitsidianakis
  Cc: qemu-block, Alberto Garcia, qemu-devel, Stefan Hajnoczi

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

On 08/03/2017 03:07 AM, Kevin Wolf wrote:
> Am 31.07.2017 um 11:54 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 syntax
>> -drive driver=throttle,file.filename=foo.qcow2, \
>>         limits.iops-total=...,throttle-group=bar
>>
>> The configuration flags and their semantics are identical to the
>> hardcoded throttling ones.
>>
>> A node can be created referring to an existing group, and will overwrite
>> its limits if any are specified, otherwise they are retained.
>>
>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>> ---

>> +
>> +    .is_filter                          =   true,
>> +};
> 
> What about .bdrv_co_get_block_status?

And if so, do you want my byte-based block status to go in first?  (Our
two series conflict, so we need to pick who needs to rebase on top of
the other).

-- 
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: 619 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-03 11:48     ` Manos Pitsidianakis
@ 2017-08-03 12:05       ` Kevin Wolf
  0 siblings, 0 replies; 43+ messages in thread
From: Kevin Wolf @ 2017-08-03 12:05 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel, Stefan Hajnoczi, Alberto Garcia,
	qemu-block

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

Am 03.08.2017 um 13:48 hat Manos Pitsidianakis geschrieben:
> On Thu, Aug 03, 2017 at 10:07:41AM +0200, Kevin Wolf wrote:
> > Am 31.07.2017 um 11:54 hat Manos Pitsidianakis geschrieben:
> > > +/* Extract ThrottleConfig options. Assumes cfg is initialized and will be
> > > + * checked for validity.
> > > + */
> > > +static int throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg,
> > > +                                     Error **errp)
> > > +{
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL)) {
> > > +        cfg->buckets[THROTTLE_BPS_TOTAL].avg =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
> > > +                                0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ)) {
> > > +        cfg->buckets[THROTTLE_BPS_READ].avg  =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
> > > +                                0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE)) {
> > > +        cfg->buckets[THROTTLE_BPS_WRITE].avg =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
> > > +                                0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL)) {
> > > +        cfg->buckets[THROTTLE_OPS_TOTAL].avg =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
> > > +                                0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ)) {
> > > +        cfg->buckets[THROTTLE_OPS_READ].avg =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
> > > +                                0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE)) {
> > > +        cfg->buckets[THROTTLE_OPS_WRITE].avg =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
> > > +                                0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX)) {
> > > +        cfg->buckets[THROTTLE_BPS_TOTAL].max =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_TOTAL_MAX, 0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX)) {
> > > +        cfg->buckets[THROTTLE_BPS_READ].max  =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_READ_MAX, 0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX)) {
> > > +        cfg->buckets[THROTTLE_BPS_WRITE].max =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_WRITE_MAX, 0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX)) {
> > > +        cfg->buckets[THROTTLE_OPS_TOTAL].max =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_TOTAL_MAX, 0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX)) {
> > > +        cfg->buckets[THROTTLE_OPS_READ].max =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_READ_MAX, 0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX)) {
> > > +        cfg->buckets[THROTTLE_OPS_WRITE].max =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_WRITE_MAX, 0);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH)) {
> > > +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
> > > +            error_setg(errp, "%s value must be in the range [0, %u]",
> > > +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
> > > +                       UINT_MAX);
> > > +            return -1;
> > > +        }
> > > +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_TOTAL_MAX_LENGTH, 1);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH)) {
> > > +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
> > > +            error_setg(errp, "%s must be in the range [0, %u]",
> > > +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
> > > +                       UINT_MAX);
> > > +            return -1;
> > > +        }
> > > +        cfg->buckets[THROTTLE_BPS_READ].burst_length  =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_READ_MAX_LENGTH, 1);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH)) {
> > > +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
> > > +            error_setg(errp, "%s must be in the range [0, %u]",
> > > +                       THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
> > > +                       UINT_MAX);
> > > +            return -1;
> > > +        }
> > > +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_BPS_WRITE_MAX_LENGTH, 1);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH)) {
> > > +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1) > UINT_MAX) {
> > > +            error_setg(errp, "%s must be in the range [0, %u]",
> > > +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
> > > +                       UINT_MAX);
> > > +            return -1;
> > > +        }
> > > +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, 1);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH)) {
> > > +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1) > UINT_MAX) {
> > > +            error_setg(errp, "%s must be in the range [0, %u]",
> > > +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
> > > +                       UINT_MAX);
> > > +            return -1;
> > > +        }
> > > +        cfg->buckets[THROTTLE_OPS_READ].burst_length =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_READ_MAX_LENGTH, 1);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH)) {
> > > +        if (qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1) > UINT_MAX) {
> > > +            error_setg(errp, "%s must be in the range [0, %u]",
> > > +                       THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
> > > +                       UINT_MAX);
> > > +            return -1;
> > > +        }
> > > +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX
> > > +                                QEMU_OPT_IOPS_WRITE_MAX_LENGTH, 1);
> > > +    }
> > > +    if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE)) {
> > > +        cfg->op_size =
> > > +            qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
> > > +                                0);
> > > +    }
> > > +    return 0;
> > > +}
> > 
> > This function is very repetitive, but each block is long enough that
> > you have to look closely to review whether the right constants are used
> > everywhere.
> > 
> > Maybe this could become a bit more readable with a macro or two?
> 
> How about this?
> 
> 
> #   #define IF_OPT_SET(rvalue,opt_name) \
> #       if (qemu_opt_get(opts, THROTTLE_OPT_PREFIX opt_name)) { \
> #           rvalue = qemu_opt_get_number(opts, THROTTLE_OPT_PREFIX opt_name,
> 0); }
> 
> #       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_TOTAL].avg, QEMU_OPT_BPS_TOTAL);
> #       IF_OPT_SET(cfg->buckets[THROTTLE_BPS_READ].avg, QEMU_OPT_BPS_READ);
> [...]

Looks a lot more readable to me. :-)

If nobody objects, I'd suggest to go this way.

> > > +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);
> > > +}
> > 
> > I think we want to set BlockDriver.supported_write_flags so that passing
> > down flags is actually of any use.
> 
> Can you explain what you mean? Do you mean doing this in throttle_open():
> 
>    bs->supported_write_flags = bs->file->bs->supported_write_flags;
>    bs->supported_zero_flags = bs->file->bs->supported_zero_flags;

Yes, I think that should do the trick.

Kevin

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

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-03 11:17               ` Kevin Wolf
@ 2017-08-03 12:29                 ` Manos Pitsidianakis
  0 siblings, 0 replies; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-03 12:29 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: Stefan Hajnoczi, qemu-devel, Alberto Garcia, qemu-block

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

On Thu, Aug 03, 2017 at 01:17:01PM +0200, Kevin Wolf wrote:
>Am 03.08.2017 um 12:53 hat Stefan Hajnoczi geschrieben:
>> On Thu, Aug 03, 2017 at 10:08:01AM +0200, Kevin Wolf wrote:
>> > Am 02.08.2017 um 12:57 hat Manos Pitsidianakis geschrieben:
>> > > On Wed, Aug 02, 2017 at 11:39:22AM +0100, Stefan Hajnoczi wrote:
>> > > > On Tue, Aug 01, 2017 at 07:49:33PM +0300, Manos Pitsidianakis wrote:
>> > > > > On Tue, Aug 01, 2017 at 04:47:03PM +0100, Stefan Hajnoczi wrote:
>> > > > > > On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
>> > > > > > > ThrottleGroup is converted to an object. This will allow the future
>> > > > > > > throttle block filter drive easy creation and configuration of throttle
>> > > > > > > groups in QMP and cli.
>> > > > > > >
>> > > > > > > A new QAPI struct, ThrottleLimits, is introduced to provide a shared
>> > > > > > > struct for all throttle configuration needs in QMP.
>> > > > > > >
>> > > > > > > ThrottleGroups can be created via CLI as
>> > > > > > >     -object throttle-group,id=foo,x-iops-total=100,x-..
>> > > > > > > where x-* are individual limit properties. Since we can't add non-scalar
>> > > > > > > properties in -object this interface must be used instead. However,
>> > > > > > > setting these properties must be disabled after initialization because
>> > > > > > > certain combinations of limits are forbidden and thus configuration
>> > > > > > > changes should be done in one transaction. The individual properties
>> > > > > > > will go away when support for non-scalar values in CLI is implemented
>> > > > > > > and thus are marked as experimental.
>> > > > > > >
>> > > > > > > ThrottleGroup also has a `limits` property that uses the ThrottleLimits
>> > > > > > > struct.  It can be used to create ThrottleGroups or set the
>> > > > > > > configuration in existing groups as follows:
>> > > > > > >
>> > > > > > > { "execute": "object-add",
>> > > > > > >   "arguments": {
>> > > > > > >     "qom-type": "throttle-group",
>> > > > > > >     "id": "foo",
>> > > > > > >     "props" : {
>> > > > > > >       "limits": {
>> > > > > > >           "iops-total": 100
>> > > > > > >       }
>> > > > > > >     }
>> > > > > > >   }
>> > > > > > > }
>> > > > > > > { "execute" : "qom-set",
>> > > > > > >     "arguments" : {
>> > > > > > >         "path" : "foo",
>> > > > > > >         "property" : "limits",
>> > > > > > >         "value" : {
>> > > > > > >             "iops-total" : 99
>> > > > > > >         }
>> > > > > > >     }
>> > > > > > > }
>> > > > > > >
>> > > > > > > This also means a group's configuration can be fetched with qom-get.
>> > > > > > >
>> > > > > > > ThrottleGroups can be anonymous which means they can't get accessed by
>> > > > > > > other users ie they will always be units instead of group (Because they
>> > > > > > > have one ThrottleGroupMember).
>> > > > > >
>> > > > > > blockdev.c automatically assigns -drive id= to the group name if
>> > > > > > throttling.group= wasn't given.  So who will use anonymous single-drive
>> > > > > > mode?
>> > > > >
>> > > > > Manual filter nodes. It's possible to not pass a group name value and the
>> > > > > resulting group will be anonymous. Are you suggesting to move this change to
>> > > > > the throttle filter patch?
>> > > >
>> > > > What is the use case?  Human users will stick to the legacy syntax
>> > > > because it's convenient.  Management tools will use the filter
>> > > > explicitly in the future, and it's easy for them to choose a name.
>> > > >
>> > > > Unless there is a need for this case I'd prefer to make the group name
>> > > > mandatory.  That way there are less code paths to worry about.
>> > >
>> > > I think Kevin requested this though I don't really remember the use case.
>> >
>> > There is no legacy syntax for putting a throttle node anywhere but at
>> > the root of a BlockBackend. If you want to throttle e.g. only a specific
>> > backing file, you need to manually create a throttle node.
>> >
>> > (We tend to forget this occasionally, but the work you're doing is not
>> > only cleanup just for fun, but it's actually new features that enable
>> > new use cases by making everything more flexible.)
>>
>> It's not clear to me from your reply whether you support anonymous
>> throttle groups or not.  It is possible to throttle arbitrary nodes in
>> the graph either way.
>
>I think it would be nice for human users to have them, but on second
>thought you're right that it's just syntactic sugar for an explicit
>-object, so if you think there is any difficulty with supporting
>anonymous groups, feel free to drop them.
>
>Hm, actually... Does this mean that then the whole 'limits' option in
>the throttle driver could go away, with all of the parsing code, and the
>group name becomes mandatory? That certainly does look tempting.


Anonymous groups don't pose any difficulty. The only problem with groups 
not created with -object or object-add in general is that their limits 
cannot be set with qom-set afterwards; the throttle node will require a 
reopen or replacement with a new one. This is a good argument against 
doing throttle group manipulation in the driver. I don't know if that's 
very user friendly though.

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

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

* Re: [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
  2017-08-03  8:07   ` Kevin Wolf
@ 2017-08-03 13:24     ` Manos Pitsidianakis
  2017-08-03 13:32       ` Kevin Wolf
  0 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-03 13:24 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, Stefan Hajnoczi, Alberto Garcia, qemu-block

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

On Thu, Aug 03, 2017 at 10:07:50AM +0200, Kevin Wolf wrote:
>Am 31.07.2017 um 11:54 hat Manos Pitsidianakis geschrieben:
>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>
>I would add at least two more cases:
>
>* Both limits and throttle-group are given in blockdev-add
This exists in the "property changes in ThrottleGroup" section,

>* limits and throttle-group are both missing
this creates an anonymous group with no limits. Should we fail at this 
case? 
>
>It would also be nice to test that query-block reflects the new throttle
>group limits correctly when they are changed after the fact.

This belongs to the remove legacy patch, since query-block displays the 
legacy limits.

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

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

* Re: [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
  2017-08-03 13:24     ` Manos Pitsidianakis
@ 2017-08-03 13:32       ` Kevin Wolf
  2017-08-03 13:52         ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Kevin Wolf @ 2017-08-03 13:32 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel, Stefan Hajnoczi, Alberto Garcia,
	qemu-block

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

Am 03.08.2017 um 15:24 hat Manos Pitsidianakis geschrieben:
> On Thu, Aug 03, 2017 at 10:07:50AM +0200, Kevin Wolf wrote:
> > Am 31.07.2017 um 11:54 hat Manos Pitsidianakis geschrieben:
> > > Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
> > 
> > I would add at least two more cases:
> > 
> > * Both limits and throttle-group are given in blockdev-add
> This exists in the "property changes in ThrottleGroup" section,

You're right, I missed this. The test result shows that this command
succeeds. Do we really want to allow other nodes to be affected with a
blockdev-add? Wouldn't it be cleaner to just forbid the combination of
limits and throtte-group?

> > * limits and throttle-group are both missing
> this creates an anonymous group with no limits. Should we fail at this case?

I'm not sure, you could argue either way. But there should be a test to
check that the semantics won't change.

If we're going with Stefan's suggestion that anonymous groups shouldn't
exist, then the question is moot anyway.

> > It would also be nice to test that query-block reflects the new throttle
> > group limits correctly when they are changed after the fact.
> 
> This belongs to the remove legacy patch, since query-block displays the
> legacy limits.

Ok, fair enough.

Kevin

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

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

* Re: [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
  2017-08-03 13:32       ` Kevin Wolf
@ 2017-08-03 13:52         ` Manos Pitsidianakis
  0 siblings, 0 replies; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-03 13:52 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, Stefan Hajnoczi, Alberto Garcia, qemu-block

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

On Thu, Aug 03, 2017 at 03:32:58PM +0200, Kevin Wolf wrote:
>Am 03.08.2017 um 15:24 hat Manos Pitsidianakis geschrieben:
>> On Thu, Aug 03, 2017 at 10:07:50AM +0200, Kevin Wolf wrote:
>> > Am 31.07.2017 um 11:54 hat Manos Pitsidianakis geschrieben:
>> > > Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>> >
>> > I would add at least two more cases:
>> >
>> > * Both limits and throttle-group are given in blockdev-add
>> This exists in the "property changes in ThrottleGroup" section,
>
>You're right, I missed this. The test result shows that this command
>succeeds. Do we really want to allow other nodes to be affected with a
>blockdev-add? Wouldn't it be cleaner to just forbid the combination of
>limits and throtte-group?

So basically only anonymous, immutable groups can be created through the 
driver then. All other shared group configurations must be explicitly 
created with an -object / object-add syntax. I think this is a neat 
separation and compromise if we allow anonymous groups. If not, we can 
ignore limits on the throttle driver.


>> > * limits and throttle-group are both missing
>> this creates an anonymous group with no limits. Should we fail at this case?
>
>I'm not sure, you could argue either way. But there should be a test to
>check that the semantics won't change.
>
>If we're going with Stefan's suggestion that anonymous groups shouldn't
>exist, then the question is moot anyway.
>
>> > It would also be nice to test that query-block reflects the new throttle
>> > group limits correctly when they are changed after the fact.
>>
>> This belongs to the remove legacy patch, since query-block displays the
>> legacy limits.
>
>Ok, fair enough.
>
>Kevin



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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-03 11:58     ` Eric Blake
@ 2017-08-03 13:56       ` Manos Pitsidianakis
  0 siblings, 0 replies; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-03 13:56 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, qemu-block, Alberto Garcia, qemu-devel, Stefan Hajnoczi

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

On Thu, Aug 03, 2017 at 06:58:30AM -0500, Eric Blake wrote:
>On 08/03/2017 03:07 AM, Kevin Wolf wrote:
>> Am 31.07.2017 um 11:54 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 syntax
>>> -drive driver=throttle,file.filename=foo.qcow2, \
>>>         limits.iops-total=...,throttle-group=bar
>>>
>>> The configuration flags and their semantics are identical to the
>>> hardcoded throttling ones.
>>>
>>> A node can be created referring to an existing group, and will overwrite
>>> its limits if any are specified, otherwise they are retained.
>>>
>>> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
>>> ---
>
>>> +
>>> +    .is_filter                          =   true,
>>> +};
>>
>> What about .bdrv_co_get_block_status?
>
>And if so, do you want my byte-based block status to go in first?  (Our
>two series conflict, so we need to pick who needs to rebase on top of
>the other).

No problem. My patch is in Kevin's branch for 2.11.  Feel free to merge 
first if needed, I can rebase my patch if you do. 

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

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

* Re: [Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
@ 2017-08-04 11:59   ` Alberto Garcia
  0 siblings, 0 replies; 43+ messages in thread
From: Alberto Garcia @ 2017-08-04 11:59 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, qemu-block

On Mon 31 Jul 2017 11:54:37 AM CEST, Manos Pitsidianakis wrote:
> This commit 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. This is done by gathering ThrottleGroup membership
> details from BlockBackendPublic into ThrottleGroupMember and refactoring
> existing code to use the structure.
>
> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

Reviewed-by: Alberto Garcia <berto@igalia.com>

Berto

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

* Re: [Qemu-devel] [PATCH v3 2/7] block: add aio_context field in ThrottleGroupMember
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 2/7] block: add aio_context field in ThrottleGroupMember Manos Pitsidianakis
@ 2017-08-04 12:14   ` Alberto Garcia
  0 siblings, 0 replies; 43+ messages in thread
From: Alberto Garcia @ 2017-08-04 12:14 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, qemu-block

On Mon 31 Jul 2017 11:54:38 AM 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.
>
> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

Reviewed-by: Alberto Garcia <berto@igalia.com>

Berto

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

* Re: [Qemu-devel] [PATCH v3 3/7] block: tidy ThrottleGroupMember initializations
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 3/7] block: tidy ThrottleGroupMember initializations Manos Pitsidianakis
@ 2017-08-04 12:35   ` Alberto Garcia
  0 siblings, 0 replies; 43+ messages in thread
From: Alberto Garcia @ 2017-08-04 12:35 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, qemu-block

On Mon 31 Jul 2017 11:54:39 AM CEST, Manos Pitsidianakis wrote:
> Move the CoMutex and CoQueue inits inside throttle_group_register_tgm()
> which is called whenever a ThrottleGroupMember is initialized. There's
> no need for them to be separate.
>
> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>

Reviewed-by: Alberto Garcia <berto@igalia.com>

Berto

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

* Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
  2017-08-02 10:57         ` Manos Pitsidianakis
  2017-08-02 14:43           ` Stefan Hajnoczi
  2017-08-03  8:08           ` Kevin Wolf
@ 2017-08-08 13:01           ` Alberto Garcia
  2 siblings, 0 replies; 43+ messages in thread
From: Alberto Garcia @ 2017-08-08 13:01 UTC (permalink / raw)
  To: Manos Pitsidianakis, Stefan Hajnoczi; +Cc: qemu-devel, Kevin Wolf, qemu-block

On Wed 02 Aug 2017 12:57:04 PM CEST, Manos Pitsidianakis wrote:
>> At the moment I think throttle_groups_lock isn't strictly needed
>> because incref/decref callers hold the QEMU global mutex anyway.
>>
>> But code accessing throttle_groups still has to be disciplined.
>> Since throttle_groups_lock exists, please use it consistently in all
>> code paths.
>>
>> Alternatively you could remove the lock and document that
>> throttle_groups is protected by the global mutex.  What we can't do
>> is sometimes use throttle_groups_lock and sometimes not use it.
>
> If we use throttle_groups_lock in throttle_group_obj_init() then we
> must give it up in throttle_group_incref() and retake it in
> throttle_group_obj_init(). Maybe indeed it's better to drop
> throttle_groups_lock altogether, since the ThrottleGroup refcounting
> always happens in a QMP or startup/cleanup context.

I checked the code and I also don't see any manipulation of the group
list outside the global mutex, so you can remove throttle_groups_lock,
but please document very clearly that all these calls can only happen
when they are protected by the global mutex.

Berto

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver Manos Pitsidianakis
  2017-08-01 16:14   ` Stefan Hajnoczi
  2017-08-03  8:07   ` Kevin Wolf
@ 2017-08-08 13:13   ` Alberto Garcia
  2017-08-08 13:45     ` Manos Pitsidianakis
  2 siblings, 1 reply; 43+ messages in thread
From: Alberto Garcia @ 2017-08-08 13:13 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: Kevin Wolf, Stefan Hajnoczi, qemu-block

On Mon 31 Jul 2017 11:54:41 AM CEST, Manos Pitsidianakis wrote:
> 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 syntax
> -drive driver=throttle,file.filename=foo.qcow2, \
>         limits.iops-total=...,throttle-group=bar

Sorry for not having noticed this earlier, but can't you define the
throttling group (and its limits) using -object throttle-group ... as
shown in the previous patch, and simply reference it here? Or would we
have two alternative ways of setting the throttling limits?

What happens if you have many -drive lines each one with a different set
of limits but with the same throttling group?

Berto

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-08 13:13   ` Alberto Garcia
@ 2017-08-08 13:45     ` Manos Pitsidianakis
  2017-08-08 14:53       ` Alberto Garcia
  0 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-08 13:45 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Tue, Aug 08, 2017 at 03:13:36PM +0200, Alberto Garcia wrote:
>On Mon 31 Jul 2017 11:54:41 AM CEST, Manos Pitsidianakis wrote:
>> 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 syntax
>> -drive driver=throttle,file.filename=foo.qcow2, \
>>         limits.iops-total=...,throttle-group=bar
>
>Sorry for not having noticed this earlier, but can't you define the
>throttling group (and its limits) using -object throttle-group ... as
>shown in the previous patch, and simply reference it here? Or would we
>have two alternative ways of setting the throttling limits?
>
>What happens if you have many -drive lines each one with a different set
>of limits but with the same throttling group?

The limits of the last one to be processed will win. Quoting a reply I 
made to Kevin on the interface test patch:

>> You're right, I missed this. The test result shows that this command
>> succeeds. Do we really want to allow other nodes to be affected with a
>> blockdev-add? Wouldn't it be cleaner to just forbid the combination of
>> limits and throtte-group?
>
>So basically only anonymous, immutable groups can be created through the 
>driver then. All other shared group configurations must be explicitly 
>created with an -object / object-add syntax. I think this is a neat 
>separation and compromise if we allow anonymous groups. If not, we can 
>ignore limits on the throttle driver.

So basically if we have anonymous groups, we accept limits in the driver 
options but only without a group-name. Without anonymous groups, we 
remove limits from the driver options and only use the 
object-add/-object commands to create throttle groups. Does this sound 
like a good idea? It will be more verbose for the human user. One 
advantage: all throttle groups can then be managed through 
qom-set/qom-get since they are owned by the qom tree.

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-08 13:45     ` Manos Pitsidianakis
@ 2017-08-08 14:53       ` Alberto Garcia
  2017-08-08 14:56         ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Alberto Garcia @ 2017-08-08 14:53 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

On Tue 08 Aug 2017 03:45:44 PM CEST, Manos Pitsidianakis wrote:
> On Tue, Aug 08, 2017 at 03:13:36PM +0200, Alberto Garcia wrote:
>>On Mon 31 Jul 2017 11:54:41 AM CEST, Manos Pitsidianakis wrote:
>>> 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 syntax
>>> -drive driver=throttle,file.filename=foo.qcow2, \
>>>         limits.iops-total=...,throttle-group=bar
>>
>>Sorry for not having noticed this earlier, but can't you define the
>>throttling group (and its limits) using -object throttle-group ... as
>>shown in the previous patch, and simply reference it here? Or would we
>>have two alternative ways of setting the throttling limits?
>>
>>What happens if you have many -drive lines each one with a different set
>>of limits but with the same throttling group?
>
> The limits of the last one to be processed will win.

That's what the current throttling API does, and I tend to agree that
it's not completely straightforward (a few people have asked me the same
question since this feature is available).

If we're going to add a new API we could eliminate this ambiguity. After
all the previous -drive throttling.iops-total=... would still be
available, wouldn't it?

> So basically if we have anonymous groups, we accept limits in the
> driver options but only without a group-name.

In the commit message you do however have limits and a group name, is
that a mistake?

    -drive driver=throttle,file.filename=foo.qcow2, \
           limits.iops-total=...,throttle-group=bar

Berto

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-08 14:53       ` Alberto Garcia
@ 2017-08-08 14:56         ` Manos Pitsidianakis
  2017-08-08 15:04           ` Alberto Garcia
  0 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-08 14:56 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Tue, Aug 08, 2017 at 04:53:08PM +0200, Alberto Garcia wrote:
>On Tue 08 Aug 2017 03:45:44 PM CEST, Manos Pitsidianakis wrote:
>> On Tue, Aug 08, 2017 at 03:13:36PM +0200, Alberto Garcia wrote:
>>>On Mon 31 Jul 2017 11:54:41 AM CEST, Manos Pitsidianakis wrote:
>>>> 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 syntax
>>>> -drive driver=throttle,file.filename=foo.qcow2, \
>>>>         limits.iops-total=...,throttle-group=bar
>>>
>>>Sorry for not having noticed this earlier, but can't you define the
>>>throttling group (and its limits) using -object throttle-group ... as
>>>shown in the previous patch, and simply reference it here? Or would we
>>>have two alternative ways of setting the throttling limits?
>>>
>>>What happens if you have many -drive lines each one with a different set
>>>of limits but with the same throttling group?
>>
>> The limits of the last one to be processed will win.
>
>That's what the current throttling API does, and I tend to agree that
>it's not completely straightforward (a few people have asked me the same
>question since this feature is available).
>
>If we're going to add a new API we could eliminate this ambiguity. After
>all the previous -drive throttling.iops-total=... would still be
>available, wouldn't it?

Indeed, it already is.

>
>> So basically if we have anonymous groups, we accept limits in the
>> driver options but only without a group-name.
>
>In the commit message you do however have limits and a group name, is
>that a mistake?
>
>    -drive driver=throttle,file.filename=foo.qcow2, \
>           limits.iops-total=...,throttle-group=bar
>

Sorry this wasn't clear, I'm actually proposing to remove limits from 
the throttle driver options and only create/config throttle groups via 
-object/object-add.

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-08 14:56         ` Manos Pitsidianakis
@ 2017-08-08 15:04           ` Alberto Garcia
  2017-08-09  9:36             ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Alberto Garcia @ 2017-08-08 15:04 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

On Tue 08 Aug 2017 04:56:20 PM CEST, Manos Pitsidianakis wrote:
>>> So basically if we have anonymous groups, we accept limits in the
>>> driver options but only without a group-name.
>>
>>In the commit message you do however have limits and a group name, is
>>that a mistake?
>>
>>    -drive driver=throttle,file.filename=foo.qcow2, \
>>           limits.iops-total=...,throttle-group=bar
>
> Sorry this wasn't clear, I'm actually proposing to remove limits from
> the throttle driver options and only create/config throttle groups via
> -object/object-add.

Sorry I think it was me who misunderstood :-) Anyway in the new
command-line API I would be more inclined to have limits defined using
"-object throttle-group" and -drive would only reference the group id.

I understand that this implies that it wouldn't be possible to create
anonymous groups (at least not from the command line), is that a
problem?

Berto

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-08 15:04           ` Alberto Garcia
@ 2017-08-09  9:36             ` Manos Pitsidianakis
  2017-08-09 12:36               ` Alberto Garcia
  0 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-09  9:36 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Tue, Aug 08, 2017 at 05:04:48PM +0200, Alberto Garcia wrote:
>On Tue 08 Aug 2017 04:56:20 PM CEST, Manos Pitsidianakis wrote:
>>>> So basically if we have anonymous groups, we accept limits in the
>>>> driver options but only without a group-name.
>>>
>>>In the commit message you do however have limits and a group name, is
>>>that a mistake?
>>>
>>>    -drive driver=throttle,file.filename=foo.qcow2, \
>>>           limits.iops-total=...,throttle-group=bar
>>
>> Sorry this wasn't clear, I'm actually proposing to remove limits from
>> the throttle driver options and only create/config throttle groups via
>> -object/object-add.
>
>Sorry I think it was me who misunderstood :-) Anyway in the new
>command-line API I would be more inclined to have limits defined using
>"-object throttle-group" and -drive would only reference the group id.
>
>I understand that this implies that it wouldn't be possible to create
>anonymous groups (at least not from the command line), is that a
>problem?
>

We can accept anonymous groups if a user specifies limits but not a 
group name in the throttle driver. (The only case where limits would be 
acccepted)

Not creating eponymous throttle groups via the throttle driver means we 
don't need throttle_groups anymore, since even anonymous ones don't need 
to be accounted for in a list. I will send a new revision for this 
series but I can make this change in a later patch if everyone agrees.

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-09  9:36             ` Manos Pitsidianakis
@ 2017-08-09 12:36               ` Alberto Garcia
  2017-08-09 13:42                 ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Alberto Garcia @ 2017-08-09 12:36 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

On Wed 09 Aug 2017 11:36:12 AM CEST, Manos Pitsidianakis wrote:
> On Tue, Aug 08, 2017 at 05:04:48PM +0200, Alberto Garcia wrote:
>>On Tue 08 Aug 2017 04:56:20 PM CEST, Manos Pitsidianakis wrote:
>>>>> So basically if we have anonymous groups, we accept limits in the
>>>>> driver options but only without a group-name.
>>>>
>>>>In the commit message you do however have limits and a group name, is
>>>>that a mistake?
>>>>
>>>>    -drive driver=throttle,file.filename=foo.qcow2, \
>>>>           limits.iops-total=...,throttle-group=bar
>>>
>>> Sorry this wasn't clear, I'm actually proposing to remove limits from
>>> the throttle driver options and only create/config throttle groups via
>>> -object/object-add.
>>
>>Sorry I think it was me who misunderstood :-) Anyway in the new
>>command-line API I would be more inclined to have limits defined using
>>"-object throttle-group" and -drive would only reference the group id.
>>
>>I understand that this implies that it wouldn't be possible to create
>>anonymous groups (at least not from the command line), is that a
>>problem?
>
> We can accept anonymous groups if a user specifies limits but not a
> group name in the throttle driver. (The only case where limits would
> be acccepted)

Yeah but that's only if we have the limits.iops-total=... options in the
throttle driver. If we "remove limits from the throttle driver options
and only create/config throttle groups via -object/object-add" we cannot
do that.

> Not creating eponymous throttle groups via the throttle driver means
> we don't need throttle_groups anymore, since even anonymous ones don't
> need to be accounted for in a list.

I don't follow you here, how else do you get a group by its name?

Berto

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-09 12:36               ` Alberto Garcia
@ 2017-08-09 13:42                 ` Manos Pitsidianakis
  2017-08-09 14:45                   ` Alberto Garcia
  0 siblings, 1 reply; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-09 13:42 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

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

On Wed, Aug 09, 2017 at 02:36:20PM +0200, Alberto Garcia wrote:
>On Wed 09 Aug 2017 11:36:12 AM CEST, Manos Pitsidianakis wrote:
>> On Tue, Aug 08, 2017 at 05:04:48PM +0200, Alberto Garcia wrote:
>>>On Tue 08 Aug 2017 04:56:20 PM CEST, Manos Pitsidianakis wrote:
>>>>>> So basically if we have anonymous groups, we accept limits in the
>>>>>> driver options but only without a group-name.
>>>>>
>>>>>In the commit message you do however have limits and a group name, is
>>>>>that a mistake?
>>>>>
>>>>>    -drive driver=throttle,file.filename=foo.qcow2, \
>>>>>           limits.iops-total=...,throttle-group=bar
>>>>
>>>> Sorry this wasn't clear, I'm actually proposing to remove limits from
>>>> the throttle driver options and only create/config throttle groups via
>>>> -object/object-add.
>>>
>>>Sorry I think it was me who misunderstood :-) Anyway in the new
>>>command-line API I would be more inclined to have limits defined using
>>>"-object throttle-group" and -drive would only reference the group id.
>>>
>>>I understand that this implies that it wouldn't be possible to create
>>>anonymous groups (at least not from the command line), is that a
>>>problem?
>>
>> We can accept anonymous groups if a user specifies limits but not a
>> group name in the throttle driver. (The only case where limits would
>> be acccepted)
>
>Yeah but that's only if we have the limits.iops-total=... options in the
>throttle driver. If we "remove limits from the throttle driver options
>and only create/config throttle groups via -object/object-add" we 
>cannot
>do that.

We can check that groups is not defined at the same time as limits,
>
>> Not creating eponymous throttle groups via the throttle driver means
>> we don't need throttle_groups anymore, since even anonymous ones don't
>> need to be accounted for in a list.
>
>I don't follow you here, how else do you get a group by its name?

If all eponymous groups are managed by the QOM tree, we should be able 
to iterate over the object root container for all ThrottleGroups just 
like qmp_query_iothreads() in iothread.c

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

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-09 13:42                 ` Manos Pitsidianakis
@ 2017-08-09 14:45                   ` Alberto Garcia
  2017-08-09 15:39                     ` Kevin Wolf
  0 siblings, 1 reply; 43+ messages in thread
From: Alberto Garcia @ 2017-08-09 14:45 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: qemu-devel, Kevin Wolf, Stefan Hajnoczi, qemu-block

On Wed 09 Aug 2017 03:42:07 PM CEST, Manos Pitsidianakis wrote:
> On Wed, Aug 09, 2017 at 02:36:20PM +0200, Alberto Garcia wrote:
>>On Wed 09 Aug 2017 11:36:12 AM CEST, Manos Pitsidianakis wrote:
>>> On Tue, Aug 08, 2017 at 05:04:48PM +0200, Alberto Garcia wrote:
>>>>On Tue 08 Aug 2017 04:56:20 PM CEST, Manos Pitsidianakis wrote:
>>>>>>> So basically if we have anonymous groups, we accept limits in the
>>>>>>> driver options but only without a group-name.
>>>>>>
>>>>>>In the commit message you do however have limits and a group name, is
>>>>>>that a mistake?
>>>>>>
>>>>>>    -drive driver=throttle,file.filename=foo.qcow2, \
>>>>>>           limits.iops-total=...,throttle-group=bar
>>>>>
>>>>> Sorry this wasn't clear, I'm actually proposing to remove limits from
>>>>> the throttle driver options and only create/config throttle groups via
>>>>> -object/object-add.
>>>>
>>>>Sorry I think it was me who misunderstood :-) Anyway in the new
>>>>command-line API I would be more inclined to have limits defined using
>>>>"-object throttle-group" and -drive would only reference the group id.
>>>>
>>>>I understand that this implies that it wouldn't be possible to create
>>>>anonymous groups (at least not from the command line), is that a
>>>>problem?
>>>
>>> We can accept anonymous groups if a user specifies limits but not a
>>> group name in the throttle driver. (The only case where limits would
>>> be acccepted)
>>
>>Yeah but that's only if we have the limits.iops-total=... options in the
>>throttle driver. If we "remove limits from the throttle driver options
>>and only create/config throttle groups via -object/object-add" we 
>>cannot
>>do that.
>
> We can check that groups is not defined at the same time as limits,

I'm not sure if I'm following the conversation anymore :-) let's try to
recap:

  a) Groups are defined like this (with the current patches):

     -object throttle-group,id=foo,x-iops-total=100,x-..

  b) Throttle nodes are defined like this:

     -drive driver=throttle,file.filename=foo.qcow2, \
            limits.iops-total=...,throttle-group=bar

  c) Therefore limits can be defined either in (a) or (b)

  d) If we omit throttle-group=... in (b), we would create an anonymous
     group.

  e) The -drive syntax from (b) has the "problem" that it's possible to
     define several nodes with the same throttling group but different
     limits. The last one would win (as the legacy syntax does), but
     that's not something completely straightforward for the end user.

  f) The syntax from (b) also has the problem that there's one more
     place that needs code to parse throttling limits.

  g) We can solve (e) and (f) if we remove the limits.* options
     altogether from the throttling filter. In that case you would need
     to define a throttle-group object and use the throttle-group option
     of the filter node in all cases.

  h) If we remove the limits.* options we cannot have anonymous groups
     anymore (at least not using this API). My question is: is it a
     problem? What would we lose? What benefits do anonymous groups
     bring us?
     
  i) We can of course maintain the limits.* options, but disallow
     throttle-group when they are present. That way we would allow
     anonymous groups and we would solve the ambiguity problem described
     in (e). My question is: is it worth it?

>>> Not creating eponymous throttle groups via the throttle driver means
>>> we don't need throttle_groups anymore, since even anonymous ones
>>> don't need to be accounted for in a list.
>>
>>I don't follow you here, how else do you get a group by its name?
>
> If all eponymous groups are managed by the QOM tree, we should be able
> to iterate over the object root container for all ThrottleGroups just
> like qmp_query_iothreads() in iothread.c

Mmm... can't we actually use the root container now already? (even with
anonymous groups I mean). Why do we need throttle_groups?

Berto

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-09 14:45                   ` Alberto Garcia
@ 2017-08-09 15:39                     ` Kevin Wolf
  2017-08-14 12:15                       ` Manos Pitsidianakis
  0 siblings, 1 reply; 43+ messages in thread
From: Kevin Wolf @ 2017-08-09 15:39 UTC (permalink / raw)
  To: Alberto Garcia
  Cc: Manos Pitsidianakis, qemu-devel, Stefan Hajnoczi, qemu-block

Am 09.08.2017 um 16:45 hat Alberto Garcia geschrieben:
> On Wed 09 Aug 2017 03:42:07 PM CEST, Manos Pitsidianakis wrote:
> > On Wed, Aug 09, 2017 at 02:36:20PM +0200, Alberto Garcia wrote:
> >>On Wed 09 Aug 2017 11:36:12 AM CEST, Manos Pitsidianakis wrote:
> >>> On Tue, Aug 08, 2017 at 05:04:48PM +0200, Alberto Garcia wrote:
> >>>>On Tue 08 Aug 2017 04:56:20 PM CEST, Manos Pitsidianakis wrote:
> >>>>>>> So basically if we have anonymous groups, we accept limits in the
> >>>>>>> driver options but only without a group-name.
> >>>>>>
> >>>>>>In the commit message you do however have limits and a group name, is
> >>>>>>that a mistake?
> >>>>>>
> >>>>>>    -drive driver=throttle,file.filename=foo.qcow2, \
> >>>>>>           limits.iops-total=...,throttle-group=bar
> >>>>>
> >>>>> Sorry this wasn't clear, I'm actually proposing to remove limits from
> >>>>> the throttle driver options and only create/config throttle groups via
> >>>>> -object/object-add.
> >>>>
> >>>>Sorry I think it was me who misunderstood :-) Anyway in the new
> >>>>command-line API I would be more inclined to have limits defined using
> >>>>"-object throttle-group" and -drive would only reference the group id.
> >>>>
> >>>>I understand that this implies that it wouldn't be possible to create
> >>>>anonymous groups (at least not from the command line), is that a
> >>>>problem?
> >>>
> >>> We can accept anonymous groups if a user specifies limits but not a
> >>> group name in the throttle driver. (The only case where limits would
> >>> be acccepted)
> >>
> >>Yeah but that's only if we have the limits.iops-total=... options in the
> >>throttle driver. If we "remove limits from the throttle driver options
> >>and only create/config throttle groups via -object/object-add" we 
> >>cannot
> >>do that.
> >
> > We can check that groups is not defined at the same time as limits,
> 
> I'm not sure if I'm following the conversation anymore :-) let's try to
> recap:
> 
>   a) Groups are defined like this (with the current patches):
> 
>      -object throttle-group,id=foo,x-iops-total=100,x-..
> 
>   b) Throttle nodes are defined like this:
> 
>      -drive driver=throttle,file.filename=foo.qcow2, \
>             limits.iops-total=...,throttle-group=bar
> 
>   c) Therefore limits can be defined either in (a) or (b)
> 
>   d) If we omit throttle-group=... in (b), we would create an anonymous
>      group.
> 
>   e) The -drive syntax from (b) has the "problem" that it's possible to
>      define several nodes with the same throttling group but different
>      limits. The last one would win (as the legacy syntax does), but
>      that's not something completely straightforward for the end user.
> 
>   f) The syntax from (b) also has the problem that there's one more
>      place that needs code to parse throttling limits.
> 
>   g) We can solve (e) and (f) if we remove the limits.* options
>      altogether from the throttling filter. In that case you would need
>      to define a throttle-group object and use the throttle-group option
>      of the filter node in all cases.
> 
>   h) If we remove the limits.* options we cannot have anonymous groups
>      anymore (at least not using this API). My question is: is it a
>      problem? What would we lose? What benefits do anonymous groups
>      bring us?

As I understand it, basically only the convenience of begin able to
specify a limit directly in -drive rather than having to create an
-object and reference its ID.

>   i) We can of course maintain the limits.* options, but disallow
>      throttle-group when they are present. That way we would allow
>      anonymous groups and we would solve the ambiguity problem described
>      in (e). My question is: is it worth it?

Maybe not. Depends on how important we consider the convenience feature.

> >>> Not creating eponymous throttle groups via the throttle driver means
> >>> we don't need throttle_groups anymore, since even anonymous ones
> >>> don't need to be accounted for in a list.
> >>
> >>I don't follow you here, how else do you get a group by its name?
> >
> > If all eponymous groups are managed by the QOM tree, we should be able
> > to iterate over the object root container for all ThrottleGroups just
> > like qmp_query_iothreads() in iothread.c
> 
> Mmm... can't we actually use the root container now already? (even with
> anonymous groups I mean). Why do we need throttle_groups?

Anonymous groups don't have a parent, so they aren't accessible through
the root container.

Kevin

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

* Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
  2017-08-09 15:39                     ` Kevin Wolf
@ 2017-08-14 12:15                       ` Manos Pitsidianakis
  0 siblings, 0 replies; 43+ messages in thread
From: Manos Pitsidianakis @ 2017-08-14 12:15 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: Alberto Garcia, qemu-devel, Stefan Hajnoczi, qemu-block

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

On Wed, Aug 09, 2017 at 05:39:42PM +0200, Kevin Wolf wrote:
>Am 09.08.2017 um 16:45 hat Alberto Garcia geschrieben:
>> On Wed 09 Aug 2017 03:42:07 PM CEST, Manos Pitsidianakis wrote:
>> > On Wed, Aug 09, 2017 at 02:36:20PM +0200, Alberto Garcia wrote:
>> >>On Wed 09 Aug 2017 11:36:12 AM CEST, Manos Pitsidianakis wrote:
>> >>> On Tue, Aug 08, 2017 at 05:04:48PM +0200, Alberto Garcia wrote:
>> >>>>On Tue 08 Aug 2017 04:56:20 PM CEST, Manos Pitsidianakis wrote:
>> >>>>>>> So basically if we have anonymous groups, we accept limits in the
>> >>>>>>> driver options but only without a group-name.
>> >>>>>>
>> >>>>>>In the commit message you do however have limits and a group name, is
>> >>>>>>that a mistake?
>> >>>>>>
>> >>>>>>    -drive driver=throttle,file.filename=foo.qcow2, \
>> >>>>>>           limits.iops-total=...,throttle-group=bar
>> >>>>>
>> >>>>> Sorry this wasn't clear, I'm actually proposing to remove limits from
>> >>>>> the throttle driver options and only create/config throttle groups via
>> >>>>> -object/object-add.
>> >>>>
>> >>>>Sorry I think it was me who misunderstood :-) Anyway in the new
>> >>>>command-line API I would be more inclined to have limits defined using
>> >>>>"-object throttle-group" and -drive would only reference the group id.
>> >>>>
>> >>>>I understand that this implies that it wouldn't be possible to create
>> >>>>anonymous groups (at least not from the command line), is that a
>> >>>>problem?
>> >>>
>> >>> We can accept anonymous groups if a user specifies limits but not a
>> >>> group name in the throttle driver. (The only case where limits would
>> >>> be acccepted)
>> >>
>> >>Yeah but that's only if we have the limits.iops-total=... options in the
>> >>throttle driver. If we "remove limits from the throttle driver options
>> >>and only create/config throttle groups via -object/object-add" we
>> >>cannot
>> >>do that.
>> >
>> > We can check that groups is not defined at the same time as limits,
>>
>> I'm not sure if I'm following the conversation anymore :-) let's try to
>> recap:
>>
>>   a) Groups are defined like this (with the current patches):
>>
>>      -object throttle-group,id=foo,x-iops-total=100,x-..
>>
>>   b) Throttle nodes are defined like this:
>>
>>      -drive driver=throttle,file.filename=foo.qcow2, \
>>             limits.iops-total=...,throttle-group=bar
>>
>>   c) Therefore limits can be defined either in (a) or (b)
>>
>>   d) If we omit throttle-group=... in (b), we would create an anonymous
>>      group.
>>
>>   e) The -drive syntax from (b) has the "problem" that it's possible to
>>      define several nodes with the same throttling group but different
>>      limits. The last one would win (as the legacy syntax does), but
>>      that's not something completely straightforward for the end user.
>>
>>   f) The syntax from (b) also has the problem that there's one more
>>      place that needs code to parse throttling limits.
>>
>>   g) We can solve (e) and (f) if we remove the limits.* options
>>      altogether from the throttling filter. In that case you would need
>>      to define a throttle-group object and use the throttle-group option
>>      of the filter node in all cases.
>>
>>   h) If we remove the limits.* options we cannot have anonymous groups
>>      anymore (at least not using this API). My question is: is it a
>>      problem? What would we lose? What benefits do anonymous groups
>>      bring us?
>
>As I understand it, basically only the convenience of begin able to
>specify a limit directly in -drive rather than having to create an
>-object and reference its ID.
>
>>   i) We can of course maintain the limits.* options, but disallow
>>      throttle-group when they are present. That way we would allow
>>      anonymous groups and we would solve the ambiguity problem described
>>      in (e). My question is: is it worth it?
>
>Maybe not. Depends on how important we consider the convenience feature.
>
>> >>> Not creating eponymous throttle groups via the throttle driver means
>> >>> we don't need throttle_groups anymore, since even anonymous ones
>> >>> don't need to be accounted for in a list.
>> >>
>> >>I don't follow you here, how else do you get a group by its name?
>> >
>> > If all eponymous groups are managed by the QOM tree, we should be able
>> > to iterate over the object root container for all ThrottleGroups just
>> > like qmp_query_iothreads() in iothread.c
>>
>> Mmm... can't we actually use the root container now already? (even with
>> anonymous groups I mean). Why do we need throttle_groups?
>
>Anonymous groups don't have a parent, so they aren't accessible through
>the root container.

Anonymous groups wouldn't have to be in any kind of list, since they
shouldn't be accessible by others and they don't have a name. They have
a refcount of 1 and are owned by their filter node.
(throttle_groups is used in throttle_group_incref() to fetch a tg with a
name and increase its reference count). They will also not be accessible
with qom-set and qom-get.

I've prepared the root container change and will include it in the next 
revision.  Do we go with anonymous groups and disallow limits.* with 
throttle-group, or do we drop anonymous groups altogether?

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

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

end of thread, other threads:[~2017-08-14 12:16 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-31  9:54 [Qemu-devel] [PATCH v3 0/7] add throttle block driver filter Manos Pitsidianakis
2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember Manos Pitsidianakis
2017-08-04 11:59   ` Alberto Garcia
2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 2/7] block: add aio_context field in ThrottleGroupMember Manos Pitsidianakis
2017-08-04 12:14   ` Alberto Garcia
2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 3/7] block: tidy ThrottleGroupMember initializations Manos Pitsidianakis
2017-08-04 12:35   ` Alberto Garcia
2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM Manos Pitsidianakis
2017-08-01 15:47   ` Stefan Hajnoczi
2017-08-01 16:49     ` Manos Pitsidianakis
2017-08-02 10:39       ` Stefan Hajnoczi
2017-08-02 10:57         ` Manos Pitsidianakis
2017-08-02 14:43           ` Stefan Hajnoczi
2017-08-03  8:08           ` Kevin Wolf
2017-08-03 10:53             ` Stefan Hajnoczi
2017-08-03 11:17               ` Kevin Wolf
2017-08-03 12:29                 ` Manos Pitsidianakis
2017-08-08 13:01           ` Alberto Garcia
2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver Manos Pitsidianakis
2017-08-01 16:14   ` Stefan Hajnoczi
2017-08-03  8:07   ` Kevin Wolf
2017-08-03 11:48     ` Manos Pitsidianakis
2017-08-03 12:05       ` Kevin Wolf
2017-08-03 11:58     ` Eric Blake
2017-08-03 13:56       ` Manos Pitsidianakis
2017-08-08 13:13   ` Alberto Garcia
2017-08-08 13:45     ` Manos Pitsidianakis
2017-08-08 14:53       ` Alberto Garcia
2017-08-08 14:56         ` Manos Pitsidianakis
2017-08-08 15:04           ` Alberto Garcia
2017-08-09  9:36             ` Manos Pitsidianakis
2017-08-09 12:36               ` Alberto Garcia
2017-08-09 13:42                 ` Manos Pitsidianakis
2017-08-09 14:45                   ` Alberto Garcia
2017-08-09 15:39                     ` Kevin Wolf
2017-08-14 12:15                       ` Manos Pitsidianakis
2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 6/7] block: add BlockDevOptionsThrottle to QAPI Manos Pitsidianakis
2017-08-01 16:16   ` Stefan Hajnoczi
2017-07-31  9:54 ` [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests Manos Pitsidianakis
2017-08-03  8:07   ` Kevin Wolf
2017-08-03 13:24     ` Manos Pitsidianakis
2017-08-03 13:32       ` Kevin Wolf
2017-08-03 13:52         ` Manos Pitsidianakis

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.