Skip to contentSkip to conversation

Block Throttle Group Support

PATCH Applied Alberto Garcia 3 participants 18 messages May 19, 2015 – Jun 3, 2015 Block layer

v8 of 9 Applied Latest is v9 →

lore
Share & tools
lore
#3 lore raw

PATCH v8 3/8 throttle: Add throttle group infrastructure tests

In reply to Alberto Garcia
---
 tests/test-throttle.c | 79 ++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 69 insertions(+), 10 deletions(-)
tests/test-throttle.c +69 -10
index 458f577..aa0e297 100644
@@ -1,10 +1,12 @@
 /*
  * Throttle infrastructure tests
  *
- * Copyright Nodalink, SARL. 2013
+ * Copyright Nodalink, EURL. 2013-2014
+ * Copyright Igalia, S.L. 2015
  *
  * Authors:
- *  Benoît Canet     <[email protected]>
+ *  Benoît Canet     <[email protected]>
+ *  Alberto Garcia   <[email protected]>
  *
  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  * See the COPYING.LIB file in the top-level directory.
@@ -15,6 +17,7 @@
 #include "block/aio.h"
 #include "qemu/throttle.h"
 #include "qemu/error-report.h"
+#include "block/throttle-groups.h"
 
 static AioContext     *ctx;
 static LeakyBucket    bkt;
@@ -500,23 +503,78 @@ static void test_accounting(void)
                                 (64.0 / 13)));
 }
 
+static void test_groups(void)
+{
+    ThrottleConfig cfg1, cfg2;
+    BlockDriverState *bdrv1, *bdrv2, *bdrv3;
+
+    bdrv1 = bdrv_new();
+    bdrv2 = bdrv_new();
+    bdrv3 = bdrv_new();
+
+    g_assert(bdrv1->throttle_state == NULL);
+    g_assert(bdrv2->throttle_state == NULL);
+    g_assert(bdrv3->throttle_state == NULL);
+
+    throttle_group_register_bs(bdrv1, "bar");
+    throttle_group_register_bs(bdrv2, "foo");
+    throttle_group_register_bs(bdrv3, "bar");
+
+    g_assert(bdrv1->throttle_state != NULL);
+    g_assert(bdrv2->throttle_state != NULL);
+    g_assert(bdrv3->throttle_state != NULL);
+
+    g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar"));
+    g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo"));
+    g_assert(bdrv1->throttle_state == bdrv3->throttle_state);
+
+    /* Setting the config of a group member affects the whole group */
+    memset(&cfg1, 0, sizeof(cfg1));
+    cfg1.buckets[THROTTLE_BPS_READ].avg  = 500000;
+    cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
+    cfg1.buckets[THROTTLE_OPS_READ].avg  = 20000;
+    cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
+    throttle_group_config(bdrv1, &cfg1);
+
+    throttle_group_get_config(bdrv1, &cfg1);
+    throttle_group_get_config(bdrv3, &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(bdrv3, &cfg1);
+
+    throttle_group_get_config(bdrv1, &cfg1);
+    throttle_group_get_config(bdrv3, &cfg2);
+    g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
+
+    throttle_group_unregister_bs(bdrv1);
+    throttle_group_unregister_bs(bdrv2);
+    throttle_group_unregister_bs(bdrv3);
+
+    g_assert(bdrv1->throttle_state == NULL);
+    g_assert(bdrv2->throttle_state == NULL);
+    g_assert(bdrv3->throttle_state == NULL);
+}
+
 int main(int argc, char **argv)
 {
-    GSource *src;
     Error *local_error = NULL;
 
-    init_clocks();
+    qemu_init_main_loop(&local_error);
+    ctx = qemu_get_aio_context();
 
-    ctx = aio_context_new(&local_error);
     if (!ctx) {
         error_report("Failed to create AIO Context: '%s'",
-                     error_get_pretty(local_error));
-        error_free(local_error);
+                     local_error ? error_get_pretty(local_error) :
+                     "Failed to initialize the QEMU main loop");
+        if (local_error) {
+            error_free(local_error);
+        }
         exit(1);
     }
-    src = aio_get_g_source(ctx);
-    g_source_attach(src, NULL);
-    g_source_unref(src);
 
     do {} while (g_main_context_iteration(NULL, false));
 
@@ -533,6 +591,7 @@ int main(int argc, char **argv)
     g_test_add_func("/throttle/config/is_valid",    test_is_valid);
     g_test_add_func("/throttle/config_functions",   test_config_functions);
     g_test_add_func("/throttle/accounting",         test_accounting);
+    g_test_add_func("/throttle/groups",             test_groups);
     return g_test_run();
 }
 
Signature · 1 line
2.1.4
#10 lore raw

PATCH v8 7/8 throttle: Update throttle infrastructure copyright

In reply to Alberto Garcia
---
 include/qemu/throttle.h | 8 +++++---
 util/throttle.c         | 8 +++++---
 2 files changed, 10 insertions(+), 6 deletions(-)
include/qemu/throttle.h +5 -3
index 2c560db..5af76f0 100644
@@ -1,10 +1,12 @@
 /*
  * QEMU throttling infrastructure
  *
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
+ * Copyright (C) Igalia, S.L. 2015
  *
- * Author:
- *   Benoît Canet <[email protected]>
+ * Authors:
+ *   Benoît Canet <[email protected]>
+ *   Alberto Garcia <[email protected]>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
util/throttle.c +5 -3
index d76a48e..706c131 100644
@@ -1,10 +1,12 @@
 /*
  * QEMU throttling infrastructure
  *
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
+ * Copyright (C) Igalia, S.L. 2015
  *
- * Author:
- *   Benoît Canet <[email protected]>
+ * Authors:
+ *   Benoît Canet <[email protected]>
+ *   Alberto Garcia <[email protected]>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
Signature · 1 line
2.1.4
#0 lore raw

v8 cover Block Throttle Group Support

The previous series no longer applies after all the code that was moved to block/io.c in 61007b316c, so I rebased it.

Other than that there are no changes.

V8:
- Rebased against the current master.

V7: https://lists.gnu.org/archive/html/qemu-devel/2015-04/msg01196.html
- Make block_set_io_throttle() keep a device in the same group if the parameter is omitted, and rewrite the code a bit to make it clearer and slightly more efficient.

- Update the documentation of block_set_io_throttle() to explain in detail how it works.

- Improve the iotest 093 to test scenarios with several devices in the same group.

- Update version number to 2.4 in the 'Since:' fields.

V6: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg06036.html
- Use qemu_init_main_loop() in the throttling tests to make sure that all BDS have an AioContext attached.

V5: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05990.html
- Fixed and expanded the tests. There's a new one that checks that configuration changes affect the whole throttling group.

- Refactored the code a bit: new function schedule_next_request()

- any_timer_armed is now reset in throttle_group_config(), it was causing qemu-iotest 093 to fail.

- Documentation improvements

V4: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05558.html
- All functions from the ThrottleGroup API now receive a BlockDriverState.

- The creation/destruction of ThrottleTimers is now handled internally.

- bdrv_io_limits_enable() no longer accepts NULL as a valid group name.

- No member of a group can access someone else's throttled_reqs queues anymore.

- No member of a group can access someone else's timers unless they have queued requests.

- After a timer is fired make sure that there was actually a request in the queue, and schedule a new one otherwise.

- Protect bdrv_swap() by locking the throttling group.

- throttle_group_co_io_limits_intercept() is now marked as a coroutine function.

- Documentation updates.

Regards,

Berto

Alberto Garcia (7):
throttle: Add throttle group infrastructure
throttle: Add throttle group infrastructure tests
throttle: Add throttle group support
throttle: acquire the ThrottleGroup lock in bdrv_swap()
throttle: add the name of the ThrottleGroup to BlockDeviceInfo
throttle: Update throttle infrastructure copyright
qemu-iotests: expand test 093 to support group throttling

Benoît Canet (1):
throttle: Extract timers from ThrottleState into a separate structure

 block.c                         |  38 ++-
 block/Makefile.objs             |   1 +
 block/io.c                      |  71 ++----
 block/qapi.c                    |   8 +-
 block/throttle-groups.c         | 496 ++++++++++++++++++++++++++++++++++++++++
 blockdev.c                      |  38 ++-
 hmp.c                           |  10 +-
 include/block/block.h           |   3 +-
 include/block/block_int.h       |   7 +-
 include/block/throttle-groups.h |  46 ++++
 include/qemu/throttle.h         |  46 ++--
 qapi/block-core.json            |  29 ++-
 qemu-options.hx                 |   1 +
 qmp-commands.hx                 |   3 +-
 tests/qemu-iotests/093          |  93 +++++---
 tests/test-throttle.c           | 161 +++++++++----
 util/throttle.c                 |  81 ++++---
 17 files changed, 927 insertions(+), 205 deletions(-)
 create mode 100644 block/throttle-groups.c
 create mode 100644 include/block/throttle-groups.h
Signature · 1 line
2.1.4
#8 lore raw

PATCH v8 6/8 throttle: add the name of the ThrottleGroup to BlockDeviceInfo

---
 block/qapi.c         | 3 +++
 hmp.c                | 6 ++++--
 qapi/block-core.json | 4 +++-
 3 files changed, 10 insertions(+), 3 deletions(-)
block/qapi.c +3 -0
index a5ac312..a738148 100644
@@ -93,6 +93,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
 
         info->has_iops_size = cfg.op_size;
         info->iops_size = cfg.op_size;
+
+        info->has_group = true;
+        info->group = g_strdup(throttle_group_get_name(bs));
     }
 
     info->write_threshold = bdrv_write_threshold_get(bs);
hmp.c +4 -2
index 31aa8f2..007feb4 100644
@@ -398,7 +398,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
                         " iops_max=%" PRId64
                         " iops_rd_max=%" PRId64
                         " iops_wr_max=%" PRId64
-                        " iops_size=%" PRId64 "\n",
+                        " iops_size=%" PRId64
+                        " group=%s\n",
                         inserted->bps,
                         inserted->bps_rd,
                         inserted->bps_wr,
@@ -411,7 +412,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
                         inserted->iops_max,
                         inserted->iops_rd_max,
                         inserted->iops_wr_max,
-                        inserted->iops_size);
+                        inserted->iops_size,
+                        inserted->group);
     }
 
     if (verbose) {
qapi/block-core.json +3 -1
index 3eae28b..15e4cda 100644
@@ -259,6 +259,8 @@
 #
 # @iops_size: #optional an I/O size in bytes (Since 1.7)
 #
+# @group: #optional throttle group name (Since 2.4)
+#
 # @cache: the cache mode used for the block device (since: 2.3)
 #
 # @write_threshold: configured write threshold for the device.
@@ -278,7 +280,7 @@
             '*bps_max': 'int', '*bps_rd_max': 'int',
             '*bps_wr_max': 'int', '*iops_max': 'int',
             '*iops_rd_max': 'int', '*iops_wr_max': 'int',
-            '*iops_size': 'int', 'cache': 'BlockdevCacheInfo',
+            '*iops_size': 'int', '*group': 'str', 'cache': 'BlockdevCacheInfo',
             'write_threshold': 'int' } }
 
 ##
Signature · 1 line
2.1.4
#12 lore raw

PATCH v8 8/8 qemu-iotests: expand test 093 to support group throttling

In reply to Alberto Garcia

This patch improves the test by attaching a different number of drives to the VM and putting them in the same throttling group. The test verifies that the I/O is evenly distributed among all members of the group, and that the limits are enforced.

By default the test is repeated 3 times with 1, 2 and 3 drives, but the maximum number of simultaneous drives is configurable.

---
 tests/qemu-iotests/093 | 93 +++++++++++++++++++++++++++++++++++---------------
 1 file changed, 65 insertions(+), 28 deletions(-)
tests/qemu-iotests/093 +65 -28
index b9096a5..c0e9e2b 100755
@@ -3,6 +3,7 @@
 # Tests for IO throttling
 #
 # Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2015 Igalia, S.L.
 #
 # 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
@@ -22,6 +23,7 @@ import iotests
 
 class ThrottleTestCase(iotests.QMPTestCase):
     test_img = "null-aio://"
+    max_drives = 3
 
     def blockstats(self, device):
         result = self.vm.qmp("query-blockstats")
@@ -32,26 +34,31 @@ class ThrottleTestCase(iotests.QMPTestCase):
         raise Exception("Device not found for blockstats: %s" % device)
 
     def setUp(self):
-        self.vm = iotests.VM().add_drive(self.test_img)
+        self.vm = iotests.VM()
+        for i in range(0, self.max_drives):
+            self.vm.add_drive(self.test_img)
         self.vm.launch()
 
     def tearDown(self):
         self.vm.shutdown()
 
-    def do_test_throttle(self, seconds, params):
+    def do_test_throttle(self, ndrives, seconds, params):
         def check_limit(limit, num):
             # IO throttling algorithm is discrete, allow 10% error so the test
             # is more robust
             return limit == 0 or \
-                   (num < seconds * limit * 1.1
-                   and num > seconds * limit * 0.9)
+                   (num < seconds * limit * 1.1 / ndrives
+                   and num > seconds * limit * 0.9 / ndrives)
 
         nsec_per_sec = 1000000000
 
-        params['device'] = 'drive0'
+        params['group'] = 'test'
 
-        result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
-        self.assert_qmp(result, 'return', {})
+        # Set the I/O throttling parameters to all drives
+        for i in range(0, ndrives):
+            params['device'] = 'drive%d' % i
+            result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
+            self.assert_qmp(result, 'return', {})
 
         # Set vm clock to a known value
         ns = seconds * nsec_per_sec
@@ -66,32 +73,60 @@ class ThrottleTestCase(iotests.QMPTestCase):
                     params['iops'] / 2,
                     params['iops_rd'])
         rd_nr *= seconds * 2
+        rd_nr /= ndrives
         wr_nr = max(params['bps'] / rq_size / 2,
                     params['bps_wr'] / rq_size,
                     params['iops'] / 2,
                     params['iops_wr'])
         wr_nr *= seconds * 2
+        wr_nr /= ndrives
+
+        # Send I/O requests to all drives
         for i in range(rd_nr):
-            self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size))
-        for i in range(wr_nr):
-            self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size))
+            for drive in range(0, ndrives):
+                self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" %
+                                    (i * rq_size, rq_size))
 
-        start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0')
+        for i in range(wr_nr):
+            for drive in range(0, ndrives):
+                self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" %
+                                    (i * rq_size, rq_size))
+
+        # We'll store the I/O stats for each drive in these arrays
+        start_rd_bytes = [0] * ndrives
+        start_rd_iops  = [0] * ndrives
+        start_wr_bytes = [0] * ndrives
+        start_wr_iops  = [0] * ndrives
+        end_rd_bytes   = [0] * ndrives
+        end_rd_iops    = [0] * ndrives
+        end_wr_bytes   = [0] * ndrives
+        end_wr_iops    = [0] * ndrives
+
+        # Read the stats before advancing the clock
+        for i in range(0, ndrives):
+            start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \
+                start_wr_iops[i] = self.blockstats('drive%d' % i)
 
         self.vm.qtest("clock_step %d" % ns)
-        end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0')
-
-        rd_bytes = end_rd_bytes - start_rd_bytes
-        rd_iops = end_rd_iops - start_rd_iops
-        wr_bytes = end_wr_bytes - start_wr_bytes
-        wr_iops = end_wr_iops - start_wr_iops
 
-        self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
-        self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
-        self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
-        self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
-        self.assertTrue(check_limit(params['iops_rd'], rd_iops))
-        self.assertTrue(check_limit(params['iops_wr'], wr_iops))
+        # Read the stats after advancing the clock
+        for i in range(0, ndrives):
+            end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \
+                end_wr_iops[i] = self.blockstats('drive%d' % i)
+
+        # Check that the I/O is within the limits and evenly distributed
+        for i in range(0, ndrives):
+            rd_bytes = end_rd_bytes[i] - start_rd_bytes[i]
+            rd_iops = end_rd_iops[i] - start_rd_iops[i]
+            wr_bytes = end_wr_bytes[i] - start_wr_bytes[i]
+            wr_iops = end_wr_iops[i] - start_wr_iops[i]
+
+            self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
+            self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
+            self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
+            self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
+            self.assertTrue(check_limit(params['iops_rd'], rd_iops))
+            self.assertTrue(check_limit(params['iops_wr'], wr_iops))
 
     def test_all(self):
         params = {"bps": 4096,
@@ -101,11 +136,13 @@ class ThrottleTestCase(iotests.QMPTestCase):
                   "iops_rd": 10,
                   "iops_wr": 10,
                  }
-        # Pick each out of all possible params and test
-        for tk in params:
-            limits = dict([(k, 0) for k in params])
-            limits[tk] = params[tk]
-            self.do_test_throttle(5, limits)
+        # Repeat the test with different numbers of drives
+        for ndrives in range(1, self.max_drives + 1):
+            # Pick each out of all possible params and test
+            for tk in params:
+                limits = dict([(k, 0) for k in params])
+                limits[tk] = params[tk] * ndrives
+                self.do_test_throttle(ndrives, 5, limits)
 
 class ThrottleTestCoroutine(ThrottleTestCase):
     test_img = "null-co://"
Signature · 1 line
2.1.4
#6 lore raw

PATCH v8 5/8 throttle: acquire the ThrottleGroup lock in bdrv_swap()

In reply to Alberto Garcia

bdrv_swap() touches the fields of a BlockDriverState that are protected by the ThrottleGroup lock. Although those fields end up in their original place, they are temporarily swapped in the process, so there's a chance that an operation on a member of the same group happening on a different thread can try to use them.

---
 block.c                         | 16 ++++++++++++++++
 block/throttle-groups.c         | 31 ++++++++++++++++++++++++++++++-
 include/block/throttle-groups.h |  3 +++
 3 files changed, 49 insertions(+), 1 deletion(-)
block.c +16 -0
index 1b249b2..290c2e3 100644
@@ -36,6 +36,7 @@
 #include "qmp-commands.h"
 #include "qemu/timer.h"
 #include "qapi-event.h"
+#include "block/throttle-groups.h"
 
 #ifdef CONFIG_BSD
 #include <sys/types.h>
@@ -1876,11 +1877,20 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
         QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list);
     }
 
+    /* If the BlockDriverState is part of a throttling group acquire
+     * its lock since we're going to mess with the protected fields.
+     * Otherwise there's no need to worry since no one else can touch
+     * them. */
+    if (bs_old->throttle_state) {
+        throttle_group_lock(bs_old);
+    }
+
     /* bs_new must be unattached and shouldn't have anything fancy enabled */
     assert(!bs_new->blk);
     assert(QLIST_EMPTY(&bs_new->dirty_bitmaps));
     assert(bs_new->job == NULL);
     assert(bs_new->io_limits_enabled == false);
+    assert(bs_new->throttle_state == NULL);
     assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
 
     tmp = *bs_new;
@@ -1898,8 +1908,14 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
     /* Check a few fields that should remain attached to the device */
     assert(bs_new->job == NULL);
     assert(bs_new->io_limits_enabled == false);
+    assert(bs_new->throttle_state == NULL);
     assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
 
+    /* Release the ThrottleGroup lock */
+    if (bs_old->throttle_state) {
+        throttle_group_unlock(bs_old);
+    }
+
     /* insert the nodes back into the graph node list if needed */
     if (bs_new->node_name[0] != '\0') {
         QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_new, node_list);
block/throttle-groups.c +30 -1
index da8c70c..efc462f 100644
@@ -33,7 +33,8 @@
  * its own locking.
  *
  * This locking is however handled internally in this file, so it's
- * transparent to outside users.
+ * mostly transparent to outside users (but see the documentation in
+ * throttle_groups_lock()).
  *
  * The whole ThrottleGroup structure is private and invisible to
  * outside users, that only use it through its ThrottleState.
@@ -459,6 +460,34 @@ void throttle_group_unregister_bs(BlockDriverState *bs)
     bs->throttle_state = NULL;
 }
 
+/* Acquire the lock of this throttling group.
+ *
+ * You won't normally need to use this. None of the functions from the
+ * ThrottleGroup API require you to acquire the lock since all of them
+ * deal with it internally.
+ *
+ * This should only be used in exceptional cases when you want to
+ * access the protected fields of a BlockDriverState directly
+ * (e.g. bdrv_swap()).
+ *
+ * @bs: a BlockDriverState that is member of the group
+ */
+void throttle_group_lock(BlockDriverState *bs)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    qemu_mutex_lock(&tg->lock);
+}
+
+/* Release the lock of this throttling group.
+ *
+ * See the comments in throttle_group_lock().
+ */
+void throttle_group_unlock(BlockDriverState *bs)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    qemu_mutex_unlock(&tg->lock);
+}
+
 static void throttle_groups_init(void)
 {
     qemu_mutex_init(&throttle_groups_lock);
include/block/throttle-groups.h +3 -0
index 322139a..fab113f 100644
@@ -40,4 +40,7 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
                                                         unsigned int bytes,
                                                         bool is_write);
 
+void throttle_group_lock(BlockDriverState *bs);
+void throttle_group_unlock(BlockDriverState *bs);
+
 #endif
Signature · 1 line
2.1.4
#2 lore raw

PATCH v8 2/8 throttle: Add throttle group infrastructure

In reply to Alberto Garcia
---
 block/Makefile.objs             |   1 +
 block/throttle-groups.c         | 261 ++++++++++++++++++++++++++++++++++++++++
 include/block/block_int.h       |   1 +
 include/block/throttle-groups.h |  39 ++++++
 4 files changed, 302 insertions(+)
 create mode 100644 block/throttle-groups.c
 create mode 100644 include/block/throttle-groups.h
block/Makefile.objs +1 -0
index 0d8c2a4..c34fd7c 100644
@@ -10,6 +10,7 @@ block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
 block-obj-$(CONFIG_POSIX) += raw-posix.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
 block-obj-y += null.o mirror.o io.o
+block-obj-y += throttle-groups.o
 
 block-obj-y += nbd.o nbd-client.o sheepdog.o
 block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block/throttle-groups.c new file +261 -0

261 lines not on this page · raw message

include/block/block_int.h +1 -0
index b05ffe2..1167fb9 100644
@@ -380,6 +380,7 @@ struct BlockDriverState {
     ThrottleTimers throttle_timers;
     CoQueue      throttled_reqs[2];
     bool         io_limits_enabled;
+    QLIST_ENTRY(BlockDriverState) round_robin;
 
     /* I/O stats (display with "info blockstats"). */
     BlockAcctStats stats;
include/block/throttle-groups.h new file +39 -0
new file mode 100644
index 0000000..b966ec7
@@ -0,0 +1,39 @@
+/*
+ * QEMU block throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ * Copyright (C) Igalia, S.L. 2015
+ *
+ * Authors:
+ *   Benoît Canet <[email protected]>
+ *   Alberto Garcia <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef THROTTLE_GROUPS_H
+#define THROTTLE_GROUPS_H
+
+#include "qemu/throttle.h"
+#include "block/block_int.h"
+
+const char *throttle_group_get_name(BlockDriverState *bs);
+
+void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg);
+void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg);
+
+void throttle_group_register_bs(BlockDriverState *bs, const char *groupname);
+void throttle_group_unregister_bs(BlockDriverState *bs);
+
+#endif
Signature · 1 line
2.1.4
#4 lore raw

PATCH v8 4/8 throttle: Add throttle group support

In reply to Alberto Garcia

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

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

---
 block.c                         |  15 +--
 block/io.c                      |  75 +++-----------
 block/qapi.c                    |   5 +-
 block/throttle-groups.c         | 214 +++++++++++++++++++++++++++++++++++++++-
 blockdev.c                      |  38 +++++--
 hmp.c                           |   4 +-
 include/block/block.h           |   3 +-
 include/block/block_int.h       |   7 +-
 include/block/throttle-groups.h |   4 +
 qapi/block-core.json            |  25 ++++-
 qemu-options.hx                 |   1 +
 qmp-commands.hx                 |   3 +-
 12 files changed, 309 insertions(+), 85 deletions(-)
block.c +9 -6
index e41ba10..1b249b2 100644
@@ -1811,15 +1811,18 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
     bs_dest->enable_write_cache = bs_src->enable_write_cache;
 
     /* i/o throttled req */
-    memcpy(&bs_dest->throttle_state,
-           &bs_src->throttle_state,
-           sizeof(ThrottleState));
+    bs_dest->throttle_state     = bs_src->throttle_state,
+    bs_dest->io_limits_enabled  = bs_src->io_limits_enabled;
+    bs_dest->pending_reqs[0]    = bs_src->pending_reqs[0];
+    bs_dest->pending_reqs[1]    = bs_src->pending_reqs[1];
+    bs_dest->throttled_reqs[0]  = bs_src->throttled_reqs[0];
+    bs_dest->throttled_reqs[1]  = bs_src->throttled_reqs[1];
+    memcpy(&bs_dest->round_robin,
+           &bs_src->round_robin,
+           sizeof(bs_dest->round_robin));
     memcpy(&bs_dest->throttle_timers,
            &bs_src->throttle_timers,
            sizeof(ThrottleTimers));
-    bs_dest->throttled_reqs[0]  = bs_src->throttled_reqs[0];
-    bs_dest->throttled_reqs[1]  = bs_src->throttled_reqs[1];
-    bs_dest->io_limits_enabled  = bs_src->io_limits_enabled;
 
     /* r/w error */
     bs_dest->on_read_error      = bs_src->on_read_error;
block/io.c +16 -59

122 lines not on this page · raw message

block/qapi.c +4 -1
index 18d2b95..a5ac312 100644
@@ -24,6 +24,7 @@
 
 #include "block/qapi.h"
 #include "block/block_int.h"
+#include "block/throttle-groups.h"
 #include "block/write-threshold.h"
 #include "qmp-commands.h"
 #include "qapi-visit.h"
@@ -65,7 +66,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
 
     if (bs->io_limits_enabled) {
         ThrottleConfig cfg;
-        throttle_get_config(&bs->throttle_state, &cfg);
+
+        throttle_group_get_config(bs, &cfg);
+
         info->bps     = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
         info->bps_rd  = cfg.buckets[THROTTLE_BPS_READ].avg;
         info->bps_wr  = cfg.buckets[THROTTLE_BPS_WRITE].avg;
block/throttle-groups.c +210 -4

254 lines not on this page · raw message

blockdev.c +29 -9
index 5eaf77e..317923d 100644
@@ -34,6 +34,7 @@
 #include "sysemu/blockdev.h"
 #include "hw/block/block.h"
 #include "block/blockjob.h"
+#include "block/throttle-groups.h"
 #include "monitor/monitor.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
@@ -357,6 +358,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
     const char *id;
     bool has_driver_specific_opts;
     BlockdevDetectZeroesOptions detect_zeroes;
+    const char *throttling_group;
 
     /* Check common options by copying from bs_opts to opts, all other options
      * stay in bs_opts for processing by bdrv_open(). */
@@ -459,6 +461,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 
     cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
 
+    throttling_group = qemu_opt_get(opts, "throttling.group");
+
     if (!check_throttle_config(&cfg, &error)) {
         error_propagate(errp, error);
         goto early_err;
@@ -547,7 +551,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 
     /* disk I/O throttling */
     if (throttle_enabled(&cfg)) {
-        bdrv_io_limits_enable(bs);
+        if (!throttling_group) {
+            throttling_group = blk_name(blk);
+        }
+        bdrv_io_limits_enable(bs, throttling_group);
         bdrv_set_io_limits(bs, &cfg);
     }
 
@@ -711,6 +718,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
 
         { "iops_size",      "throttling.iops-size" },
 
+        { "group",          "throttling.group" },
+
         { "readonly",       "read-only" },
     };
 
@@ -1951,7 +1960,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
                                bool has_iops_wr_max,
                                int64_t iops_wr_max,
                                bool has_iops_size,
-                               int64_t iops_size, Error **errp)
+                               int64_t iops_size,
+                               bool has_group,
+                               const char *group, Error **errp)
 {
     ThrottleConfig cfg;
     BlockDriverState *bs;
@@ -2004,14 +2015,19 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
     aio_context = bdrv_get_aio_context(bs);
     aio_context_acquire(aio_context);
 
-    if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
-        bdrv_io_limits_enable(bs);
-    } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
-        bdrv_io_limits_disable(bs);
-    }
-
-    if (bs->io_limits_enabled) {
+    if (throttle_enabled(&cfg)) {
+        /* Enable I/O limits if they're not enabled yet, otherwise
+         * just update the throttling group. */
+        if (!bs->io_limits_enabled) {
+            bdrv_io_limits_enable(bs, has_group ? group : device);
+        } else if (has_group) {
+            bdrv_io_limits_update_group(bs, group);
+        }
+        /* Set the new throttling configuration */
         bdrv_set_io_limits(bs, &cfg);
+    } else if (bs->io_limits_enabled) {
+        /* If all throttling settings are set to 0, disable I/O limits */
+        bdrv_io_limits_disable(bs);
     }
 
     aio_context_release(aio_context);
@@ -3190,6 +3206,10 @@ QemuOptsList qemu_common_drive_opts = {
             .type = QEMU_OPT_NUMBER,
             .help = "when limiting by iops max size of an I/O in bytes",
         },{
+            .name = "throttling.group",
+            .type = QEMU_OPT_STRING,
+            .help = "name of the block throttling group",
+        },{
             .name = "copy-on-read",
             .type = QEMU_OPT_BOOL,
             .help = "copy read data from backing file into image file",
hmp.c +3 -1
index e17852d..31aa8f2 100644
@@ -1338,7 +1338,9 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
                               false,
                               0,
                               false, /* No default I/O size */
-                              0, &err);
+                              0,
+                              false,
+                              NULL, &err);
     hmp_handle_error(mon, &err);
 }
 
include/block/block.h +2 -1
index 7d1a717..903f229 100644
@@ -173,8 +173,9 @@ void bdrv_stats_print(Monitor *mon, const QObject *data);
 void bdrv_info_stats(Monitor *mon, QObject **ret_data);
 
 /* disk I/O throttling */
-void bdrv_io_limits_enable(BlockDriverState *bs);
+void bdrv_io_limits_enable(BlockDriverState *bs, const char *group);
 void bdrv_io_limits_disable(BlockDriverState *bs);
+void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group);
 
 void bdrv_init(void);
 void bdrv_init_with_whitelist(void);
include/block/block_int.h +5 -2
index 1167fb9..3071d31 100644
@@ -376,10 +376,13 @@ struct BlockDriverState {
     unsigned int serialising_in_flight;
 
     /* I/O throttling */
-    ThrottleState throttle_state;
-    ThrottleTimers throttle_timers;
     CoQueue      throttled_reqs[2];
     bool         io_limits_enabled;
+    /* The following fields are protected by the ThrottleGroup lock.
+     * See the ThrottleGroup documentation for details. */
+    ThrottleState *throttle_state;
+    ThrottleTimers throttle_timers;
+    unsigned       pending_reqs[2];
     QLIST_ENTRY(BlockDriverState) round_robin;
 
     /* I/O stats (display with "info blockstats"). */
include/block/throttle-groups.h +4 -0
index b966ec7..322139a 100644
@@ -36,4 +36,8 @@ void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg);
 void throttle_group_register_bs(BlockDriverState *bs, const char *groupname);
 void throttle_group_unregister_bs(BlockDriverState *bs);
 
+void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
+                                                        unsigned int bytes,
+                                                        bool is_write);
+
 #endif
qapi/block-core.json +24 -1
index 863ffea..3eae28b 100644
@@ -1043,6 +1043,27 @@
 #
 # Change I/O throttle limits for a block drive.
 #
+# Since QEMU 2.4, each device with I/O limits is member of a throttle
+# group.
+#
+# If two or more devices are members of the same group, the limits
+# will apply to the combined I/O of the whole group in a round-robin
+# fashion. Therefore, setting new I/O limits to a device will affect
+# the whole group.
+#
+# The name of the group can be specified using the 'group' parameter.
+# If the parameter is unset, it is assumed to be the current group of
+# that device. If it's not in any group yet, the name of the device
+# will be used as the name for its group.
+#
+# The 'group' parameter can also be used to move a device to a
+# different group. In this case the limits specified in the parameters
+# will be applied to the new group only.
+#
+# I/O limits can be disabled by setting all of them to 0. In this case
+# the device will be removed from its group and the rest of its
+# members will no be affected. The 'group' parameter is ignored.
+#
 # @device: The name of the device
 #
 # @bps: total throughput limit in bytes per second
@@ -1071,6 +1092,8 @@
 #
 # @iops_size: #optional an I/O size in bytes (Since 1.7)
 #
+# @group: #optional throttle group name (Since 2.4)
+#
 # Returns: Nothing on success
 #          If @device is not a valid block device, DeviceNotFound
 #
@@ -1082,7 +1105,7 @@
             '*bps_max': 'int', '*bps_rd_max': 'int',
             '*bps_wr_max': 'int', '*iops_max': 'int',
             '*iops_rd_max': 'int', '*iops_wr_max': 'int',
-            '*iops_size': 'int' } }
+            '*iops_size': 'int', '*group': 'str' } }
 
 ##
 # @block-stream:
qemu-options.hx +1 -0
index ec356f6..bf38bba 100644
@@ -464,6 +464,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive,
     "       [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
     "       [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n"
     "       [[,iops_size=is]]\n"
+    "       [[,group=g]]\n"
     "                use 'file' as a drive image\n", QEMU_ARCH_ALL)
 STEXI
 @item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
qmp-commands.hx +2 -1
index 14e109e..2ad6acb 100644
@@ -1853,7 +1853,7 @@ EQMP
 
     {
         .name       = "block_set_io_throttle",
-        .args_type  = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?",
+        .args_type  = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?",
         .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle,
     },
 
@@ -1879,6 +1879,7 @@ Arguments:
 - "iops_rd_max":  read I/O operations max (json-int)
 - "iops_wr_max":  write I/O operations max (json-int)
 - "iops_size":  I/O size in bytes when limiting (json-int)
+- "group": throttle group name (json-string)
 
 Example:
 
Signature · 1 line
2.1.4
#1 lore raw

PATCH v8 1/8 throttle: Extract timers from ThrottleState into a separate structure

In reply to Alberto Garcia

From: Benoît Canet <[email protected]>

Group throttling will share ThrottleState between multiple bs. As a consequence the ThrottleState will be accessed by multiple aio context.

Timers are tied to their aio context so they must go out of the ThrottleState structure.

This commit paves the way for each bs of a common ThrottleState to have its own timer.

---
 block.c                   | 11 ++++---
 block/io.c                | 24 ++++++++------
 include/block/block_int.h |  1 +
 include/qemu/throttle.h   | 38 ++++++++++++++--------
 tests/test-throttle.c     | 82 ++++++++++++++++++++++++++---------------------
 util/throttle.c           | 73 ++++++++++++++++++++++++-----------------
 6 files changed, 135 insertions(+), 94 deletions(-)
block.c +7 -4
index 7904098..e41ba10 100644
@@ -1814,6 +1814,9 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
     memcpy(&bs_dest->throttle_state,
            &bs_src->throttle_state,
            sizeof(ThrottleState));
+    memcpy(&bs_dest->throttle_timers,
+           &bs_src->throttle_timers,
+           sizeof(ThrottleTimers));
     bs_dest->throttled_reqs[0]  = bs_src->throttled_reqs[0];
     bs_dest->throttled_reqs[1]  = bs_src->throttled_reqs[1];
     bs_dest->io_limits_enabled  = bs_src->io_limits_enabled;
@@ -1875,7 +1878,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
     assert(QLIST_EMPTY(&bs_new->dirty_bitmaps));
     assert(bs_new->job == NULL);
     assert(bs_new->io_limits_enabled == false);
-    assert(!throttle_have_timer(&bs_new->throttle_state));
+    assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
 
     tmp = *bs_new;
     *bs_new = *bs_old;
@@ -1892,7 +1895,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
     /* Check a few fields that should remain attached to the device */
     assert(bs_new->job == NULL);
     assert(bs_new->io_limits_enabled == false);
-    assert(!throttle_have_timer(&bs_new->throttle_state));
+    assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
 
     /* insert the nodes back into the graph node list if needed */
     if (bs_new->node_name[0] != '\0') {
@@ -3668,7 +3671,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
     }
 
     if (bs->io_limits_enabled) {
-        throttle_detach_aio_context(&bs->throttle_state);
+        throttle_timers_detach_aio_context(&bs->throttle_timers);
     }
     if (bs->drv->bdrv_detach_aio_context) {
         bs->drv->bdrv_detach_aio_context(bs);
@@ -3704,7 +3707,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
         bs->drv->bdrv_attach_aio_context(bs, new_context);
     }
     if (bs->io_limits_enabled) {
-        throttle_attach_aio_context(&bs->throttle_state, new_context);
+        throttle_timers_attach_aio_context(&bs->throttle_timers, new_context);
     }
 
     QLIST_FOREACH(ban, &bs->aio_notifiers, list) {
block/io.c +14 -10
index 1ce62c4..1f20c9c 100644
@@ -65,7 +65,7 @@ void bdrv_set_io_limits(BlockDriverState *bs,
 {
     int i;
 
-    throttle_config(&bs->throttle_state, cfg);
+    throttle_config(&bs->throttle_state, &bs->throttle_timers, cfg);
 
     for (i = 0; i < 2; i++) {
         qemu_co_enter_next(&bs->throttled_reqs[i]);
@@ -98,7 +98,7 @@ void bdrv_io_limits_disable(BlockDriverState *bs)
 
     bdrv_start_throttled_reqs(bs);
 
-    throttle_destroy(&bs->throttle_state);
+    throttle_timers_destroy(&bs->throttle_timers);
 }
 
 static void bdrv_throttle_read_timer_cb(void *opaque)
@@ -123,12 +123,13 @@ void bdrv_io_limits_enable(BlockDriverState *bs)
         clock_type = QEMU_CLOCK_VIRTUAL;
     }
     assert(!bs->io_limits_enabled);
-    throttle_init(&bs->throttle_state,
-                  bdrv_get_aio_context(bs),
-                  clock_type,
-                  bdrv_throttle_read_timer_cb,
-                  bdrv_throttle_write_timer_cb,
-                  bs);
+    throttle_init(&bs->throttle_state);
+    throttle_timers_init(&bs->throttle_timers,
+                         bdrv_get_aio_context(bs),
+                         clock_type,
+                         bdrv_throttle_read_timer_cb,
+                         bdrv_throttle_write_timer_cb,
+                         bs);
     bs->io_limits_enabled = true;
 }
 
@@ -142,7 +143,9 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
                                      bool is_write)
 {
     /* does this io must wait */
-    bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write);
+    bool must_wait = throttle_schedule_timer(&bs->throttle_state,
+                                             &bs->throttle_timers,
+                                             is_write);
 
     /* if must wait or any request of this type throttled queue the IO */
     if (must_wait ||
@@ -155,7 +158,8 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
 
 
     /* if the next request must wait -> do nothing */
-    if (throttle_schedule_timer(&bs->throttle_state, is_write)) {
+    if (throttle_schedule_timer(&bs->throttle_state, &bs->throttle_timers,
+                                is_write)) {
         return;
     }
 
include/block/block_int.h +1 -0
index db29b74..b05ffe2 100644
@@ -377,6 +377,7 @@ struct BlockDriverState {
 
     /* I/O throttling */
     ThrottleState throttle_state;
+    ThrottleTimers throttle_timers;
     CoQueue      throttled_reqs[2];
     bool         io_limits_enabled;
 
include/qemu/throttle.h +24 -14
index b890613..2c560db 100644
@@ -65,14 +65,17 @@ typedef struct ThrottleConfig {
 typedef struct ThrottleState {
     ThrottleConfig cfg;       /* configuration */
     int64_t previous_leak;    /* timestamp of the last leak done */
-    QEMUTimer * timers[2];    /* timers used to do the throttling */
+} ThrottleState;
+
+typedef struct ThrottleTimers {
+    QEMUTimer *timers[2];     /* timers used to do the throttling */
     QEMUClockType clock_type; /* the clock used */
 
     /* Callbacks */
     QEMUTimerCB *read_timer_cb;
     QEMUTimerCB *write_timer_cb;
     void *timer_opaque;
-} ThrottleState;
+} ThrottleTimers;
 
 /* operations on single leaky buckets */
 void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta);
@@ -86,20 +89,23 @@ bool throttle_compute_timer(ThrottleState *ts,
                             int64_t *next_timestamp);
 
 /* init/destroy cycle */
-void throttle_init(ThrottleState *ts,
-                   AioContext *aio_context,
-                   QEMUClockType clock_type,
-                   void (read_timer)(void *),
-                   void (write_timer)(void *),
-                   void *timer_opaque);
+void throttle_init(ThrottleState *ts);
+
+void throttle_timers_init(ThrottleTimers *tt,
+                          AioContext *aio_context,
+                          QEMUClockType clock_type,
+                          QEMUTimerCB *read_timer_cb,
+                          QEMUTimerCB *write_timer_cb,
+                          void *timer_opaque);
 
-void throttle_destroy(ThrottleState *ts);
+void throttle_timers_destroy(ThrottleTimers *tt);
 
-void throttle_detach_aio_context(ThrottleState *ts);
+void throttle_timers_detach_aio_context(ThrottleTimers *tt);
 
-void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context);
+void throttle_timers_attach_aio_context(ThrottleTimers *tt,
+                                        AioContext *new_context);
 
-bool throttle_have_timer(ThrottleState *ts);
+bool throttle_timers_are_initialized(ThrottleTimers *tt);
 
 /* configuration */
 bool throttle_enabled(ThrottleConfig *cfg);
@@ -108,12 +114,16 @@ bool throttle_conflicting(ThrottleConfig *cfg);
 
 bool throttle_is_valid(ThrottleConfig *cfg);
 
-void throttle_config(ThrottleState *ts, ThrottleConfig *cfg);
+void throttle_config(ThrottleState *ts,
+                     ThrottleTimers *tt,
+                     ThrottleConfig *cfg);
 
 void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg);
 
 /* usage */
-bool throttle_schedule_timer(ThrottleState *ts, bool is_write);
+bool throttle_schedule_timer(ThrottleState *ts,
+                             ThrottleTimers *tt,
+                             bool is_write);
 
 void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
 
tests/test-throttle.c +46 -36

163 lines not on this page · raw message

util/throttle.c +43 -30

140 lines not on this page · raw message

Signature · 1 line
2.1.4
#13 lore raw

Re: [Qemu-devel] [PATCH 8/8] qemu-iotests: expand test 093 to support group throttling

In reply to Alberto Garcia

tests/qemu-iotests/093 class ThrottleTestCase(iotests.QMPTestCase): · line 39

Quoted 8 lines · Alberto Garcia
tests/qemu-iotests/093 +3 -1
@@ -32,26 +34,31 @@ class ThrottleTestCase(iotests.QMPTestCase):
         raise Exception("Device not found for blockstats: %s" % device)
 
     def setUp(self):
-        self.vm = iotests.VM().add_drive(self.test_img)
+        self.vm = iotests.VM()
+        for i in range(0, self.max_drives):
+            self.vm.add_drive(self.test_img)

Unique files are required, it's not safe to write to open the same image file for writing multiple times.

This test case works with raw image files but could fail with image formats like qcow2, vmdk, etc. These image formats are not designed for multiple writers and the test could fail early due to image file corruption.

Reviewed-by on v8 4/8
#5 lore raw

Re: [Qemu-devel] [Qemu-block] [PATCH 4/8] throttle: Add throttle group support

In reply to Alberto Garcia
Quoted 11 lines · Alberto Garcia

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

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

Quoted 14 lines
---
 block.c                         |  15 +--
 block/io.c                      |  75 +++-----------
 block/qapi.c                    |   5 +-
 block/throttle-groups.c         | 214 +++++++++++++++++++++++++++++++++++++++-
 blockdev.c                      |  38 +++++--
 hmp.c                           |   4 +-
 include/block/block.h           |   3 +-
 include/block/block_int.h       |   7 +-
 include/block/throttle-groups.h |   4 +
 qapi/block-core.json            |  25 ++++-
 qemu-options.hx                 |   1 +
 qmp-commands.hx                 |   3 +-
 12 files changed, 309 insertions(+), 85 deletions(-)
Reviewed-by on v8 5/8
#7 lore raw

Re: [Qemu-devel] [PATCH 5/8] throttle: acquire the ThrottleGroup lock in bdrv_swap()

In reply to Alberto Garcia
Quoted 6 lines · Alberto Garcia

bdrv_swap() touches the fields of a BlockDriverState that are protected by the ThrottleGroup lock. Although those fields end up in their original place, they are temporarily swapped in the process, so there's a chance that an operation on a member of the same group happening on a different thread can try to use them.

Quoted 6 lines
---
 block.c                         | 16 ++++++++++++++++
 block/throttle-groups.c         | 31 ++++++++++++++++++++++++++++++-
 include/block/throttle-groups.h |  3 +++
 3 files changed, 49 insertions(+), 1 deletion(-)
#14 lore raw

Re: [Qemu-devel] [PATCH 8/8] qemu-iotests: expand test 093 to support group throttling

In reply to Stefan Hajnoczi
Quoted 8 lines · Stefan Hajnoczi

tests/qemu-iotests/093 class ThrottleTestCase(iotests.QMPTestCase): · line 39

Quoted 5 lines

def setUp(self):

tests/qemu-iotests/093 +3 -1
-        self.vm = iotests.VM().add_drive(self.test_img)
+        self.vm = iotests.VM()
+        for i in range(0, self.max_drives):
+            self.vm.add_drive(self.test_img)

Unique files are required, it's not safe to write to open the same image file for writing multiple times.

But there's no file here, self.test_img is null-aio://

Berto

Reviewed-by on the v8 series
#16 lore raw
In reply to Alberto Garcia
Quoted 90 lines · Alberto Garcia

The previous series no longer applies after all the code that was moved to block/io.c in 61007b316c, so I rebased it.

Other than that there are no changes.

V8:
- Rebased against the current master.

V7: https://lists.gnu.org/archive/html/qemu-devel/2015-04/msg01196.html
- Make block_set_io_throttle() keep a device in the same group if the parameter is omitted, and rewrite the code a bit to make it clearer and slightly more efficient.

- Update the documentation of block_set_io_throttle() to explain in detail how it works.

- Improve the iotest 093 to test scenarios with several devices in the same group.

- Update version number to 2.4 in the 'Since:' fields.

V6: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg06036.html
- Use qemu_init_main_loop() in the throttling tests to make sure that all BDS have an AioContext attached.

V5: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05990.html
- Fixed and expanded the tests. There's a new one that checks that configuration changes affect the whole throttling group.

- Refactored the code a bit: new function schedule_next_request()

- any_timer_armed is now reset in throttle_group_config(), it was causing qemu-iotest 093 to fail.

- Documentation improvements

V4: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05558.html
- All functions from the ThrottleGroup API now receive a BlockDriverState.

- The creation/destruction of ThrottleTimers is now handled internally.

- bdrv_io_limits_enable() no longer accepts NULL as a valid group name.

- No member of a group can access someone else's throttled_reqs queues anymore.

- No member of a group can access someone else's timers unless they have queued requests.

- After a timer is fired make sure that there was actually a request in the queue, and schedule a new one otherwise.

- Protect bdrv_swap() by locking the throttling group.

- throttle_group_co_io_limits_intercept() is now marked as a coroutine function.

- Documentation updates.

Regards,

Berto

Alberto Garcia (7):
throttle: Add throttle group infrastructure
throttle: Add throttle group infrastructure tests
throttle: Add throttle group support
throttle: acquire the ThrottleGroup lock in bdrv_swap()
throttle: add the name of the ThrottleGroup to BlockDeviceInfo
throttle: Update throttle infrastructure copyright
qemu-iotests: expand test 093 to support group throttling

Benoît Canet (1):
throttle: Extract timers from ThrottleState into a separate structure

 block.c                         |  38 ++-
 block/Makefile.objs             |   1 +
 block/io.c                      |  71 ++----
 block/qapi.c                    |   8 +-
 block/throttle-groups.c         | 496 ++++++++++++++++++++++++++++++++++++++++
 blockdev.c                      |  38 ++-
 hmp.c                           |  10 +-
 include/block/block.h           |   3 +-
 include/block/block_int.h       |   7 +-
 include/block/throttle-groups.h |  46 ++++
 include/qemu/throttle.h         |  46 ++--
 qapi/block-core.json            |  29 ++-
Quoted 12 lines
 qemu-options.hx                 |   1 +
 qmp-commands.hx                 |   3 +-
 tests/qemu-iotests/093          |  93 +++++---
 tests/test-throttle.c           | 161 +++++++++----
 util/throttle.c                 |  81 ++++---
 17 files changed, 927 insertions(+), 205 deletions(-)
 create mode 100644 block/throttle-groups.c
 create mode 100644 include/block/throttle-groups.h
Signature · 1 line
2.1.4
Reviewed-by
#15 lore raw

Re: [Qemu-devel] [PATCH 8/8] qemu-iotests: expand test 093 to support group throttling

In reply to Alberto Garcia
Quoted 8 lines · Alberto Garcia
Quoted 6 lines · Stefan Hajnoczi
Quoted 5 lines

def setUp(self):

tests/qemu-iotests/093 +3 -1
-        self.vm = iotests.VM().add_drive(self.test_img)
+        self.vm = iotests.VM()
+        for i in range(0, self.max_drives):
+            self.vm.add_drive(self.test_img)
Quoted 4 lines
Quoted 2 lines

Unique files are required, it's not safe to write to open the same image file for writing multiple times.

But there's no file here, self.test_img is null-aio://

You are right. ThrottleTestCase is only used in this file so there's little danger of setUp() getting called with actual files.

#17 lore raw
In reply to Alberto Garcia
Quoted 90 lines · Alberto Garcia

The previous series no longer applies after all the code that was moved to block/io.c in 61007b316c, so I rebased it.

Other than that there are no changes.

V8:
- Rebased against the current master.

V7: https://lists.gnu.org/archive/html/qemu-devel/2015-04/msg01196.html
- Make block_set_io_throttle() keep a device in the same group if the parameter is omitted, and rewrite the code a bit to make it clearer and slightly more efficient.

- Update the documentation of block_set_io_throttle() to explain in detail how it works.

- Improve the iotest 093 to test scenarios with several devices in the same group.

- Update version number to 2.4 in the 'Since:' fields.

V6: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg06036.html
- Use qemu_init_main_loop() in the throttling tests to make sure that all BDS have an AioContext attached.

V5: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05990.html
- Fixed and expanded the tests. There's a new one that checks that configuration changes affect the whole throttling group.

- Refactored the code a bit: new function schedule_next_request()

- any_timer_armed is now reset in throttle_group_config(), it was causing qemu-iotest 093 to fail.

- Documentation improvements

V4: https://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05558.html
- All functions from the ThrottleGroup API now receive a BlockDriverState.

- The creation/destruction of ThrottleTimers is now handled internally.

- bdrv_io_limits_enable() no longer accepts NULL as a valid group name.

- No member of a group can access someone else's throttled_reqs queues anymore.

- No member of a group can access someone else's timers unless they have queued requests.

- After a timer is fired make sure that there was actually a request in the queue, and schedule a new one otherwise.

- Protect bdrv_swap() by locking the throttling group.

- throttle_group_co_io_limits_intercept() is now marked as a coroutine function.

- Documentation updates.

Regards,

Berto

Alberto Garcia (7):
throttle: Add throttle group infrastructure
throttle: Add throttle group infrastructure tests
throttle: Add throttle group support
throttle: acquire the ThrottleGroup lock in bdrv_swap()
throttle: add the name of the ThrottleGroup to BlockDeviceInfo
throttle: Update throttle infrastructure copyright
qemu-iotests: expand test 093 to support group throttling

Benoît Canet (1):
throttle: Extract timers from ThrottleState into a separate structure

 block.c                         |  38 ++-
 block/Makefile.objs             |   1 +
 block/io.c                      |  71 ++----
 block/qapi.c                    |   8 +-
 block/throttle-groups.c         | 496 ++++++++++++++++++++++++++++++++++++++++
 blockdev.c                      |  38 ++-
 hmp.c                           |  10 +-
 include/block/block.h           |   3 +-
 include/block/block_int.h       |   7 +-
 include/block/throttle-groups.h |  46 ++++
 include/qemu/throttle.h         |  46 ++--
 qapi/block-core.json            |  29 ++-
Quoted 13 lines
 qemu-options.hx                 |   1 +
 qmp-commands.hx                 |   3 +-
 tests/qemu-iotests/093          |  93 +++++---
 tests/test-throttle.c           | 161 +++++++++----
 util/throttle.c                 |  81 ++++---
 17 files changed, 927 insertions(+), 205 deletions(-)
 create mode 100644 block/throttle-groups.c
 create mode 100644 include/block/throttle-groups.h
Signature · 1 line
2.1.4

Thanks, applied to my block tree:
https://github.com/stefanha/qemu/commits/block

Stefan