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,throttle-group=bar
which registers the throttle filter node with the ThrottleGroup 'bar'. The
given group must be created beforehand with object-add or -object.
Reviewed-by: Alberto Garcia
Signed-off-by: Manos Pitsidianakis
---
qapi/block-core.json| 18 ++-
include/block/throttle-groups.h | 5 +
include/qemu/throttle-options.h | 1 +
block/throttle-groups.c | 15 ++-
block/throttle.c| 250
block/Makefile.objs | 1 +
6 files changed, 288 insertions(+), 2 deletions(-)
create mode 100644 block/throttle.c
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 0bdc69aa5f..03724146a4 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -,6 +,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,20 @@
'*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
+#must already exist.
+# @file: reference to or definition of the data source block device
+# Since: 2.11
+##
+{ 'struct': 'BlockdevOptionsThrottle',
+ 'data': { 'throttle-group': 'str',
+'file' : 'BlockdevRef'
+ } }
+##
# @BlockdevOptions:
#
# Options for creating a block device. Many options are available for all
@@ -3155,6 +3170,7 @@
'replication':'BlockdevOptionsReplication',
'sheepdog': 'BlockdevOptionsSheepdog',
'ssh':'BlockdevOptionsSsh',
+ 'throttle': 'BlockdevOptionsThrottle',
'vdi':'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
'vmdk': 'BlockdevOptionsGenericCOWFormat',
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index 82f030523f..e2fd0513c4 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -76,5 +76,10 @@ void coroutine_fn
throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm
void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
AioContext *new_context);
void throttle_group_detach_aio_context(ThrottleGroupMember *tgm);
+/*
+ * throttle_group_exists() must be called under the global
+ * mutex.
+ */
+bool throttle_group_exists(const char *name);
#endif
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 \
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 5993575838..454bfcb067 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -101,6 +101,14 @@ static ThrottleGroup *throttle_group_by_name(const char
*name)
return NULL;
}
+/* This function reads throttle_groups and must be called under the global
+ * mutex.
+ */
+bool throttle_group_exists(const char *name)
+{
+return throttle_group_by_name(name) != NULL;
+}
+
/* Increments the reference count of a ThrottleGroup given its name.
*
* If no ThrottleGroup is found with the given name a new one is
@@ -543,6 +551,11 @@ void throttle_group_unregister_tgm(ThrottleGroupMember
*tgm)
ThrottleGroupMember *token;
int i;
+if (!ts) {
+/* Discard already unregistered tgm */
+return;
+}
+
assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
assert(qemu_co_queue_empty(>throttled_reqs[0]));
assert(qemu_co_queue_empty(>throttled_reqs[1]));
@@ -709,7 +722,7 @@ static void throttle_group_obj_complete(UserCreatable *obj,
Error **errp)
assert(tg->name);
/* error if name is duplicate */
-if (throttle_group_by_name(tg->name) != NULL) {
+if (throttle_group_exists(tg->name))