[Qemu-devel] [PATCH v3 7/7] qemu-iotests: add 191 for legacy throttling interface
Check that the implicit throttle filter driver node, used for compatibility with the legacy throttling interface on the BlockBackend level, works. Reviewed-by: Alberto Garcia <be...@igalia.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/191 | 138 + tests/qemu-iotests/191.out | 5 ++ tests/qemu-iotests/group | 1 + 3 files changed, 144 insertions(+) create mode 100644 tests/qemu-iotests/191 create mode 100644 tests/qemu-iotests/191.out diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 new file mode 100644 index 00..82fd0b2fe5 --- /dev/null +++ b/tests/qemu-iotests/191 @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Tests that the legacy throttling interface using an implicit throttle filter +# driver node works +# +# 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/>. +# + +import os +import iotests + +class TestLegacyThrottling(iotests.QMPTestCase): +test_img = os.path.join(iotests.test_dir, "test.img") +target_img = os.path.join(iotests.test_dir, "target.img") +base_img = os.path.join(iotests.test_dir, "base.img") + +def setUp(self): +iotests.qemu_img("create", "-f", iotests.imgfmt, self.base_img, "1G") +iotests.qemu_img("create", "-f", iotests.imgfmt, self.test_img, "-b", self.base_img) +iotests.qemu_io("-f", iotests.imgfmt, "-c", "write -P0x5d 1M 128M", self.test_img) +self.vm = iotests.VM().add_drive(self.test_img) +self.vm.launch() + +def tearDown(self): +self.do_check_throttle_node(expect=True) +params = {"device": "drive0", + "bps": 0, + "bps_rd": 0, + "bps_wr": 0, + "iops": 0, + "iops_rd": 0, + "iops_wr": 0, + } +""" +This must remove the implicit throttle_node +""" +result = self.vm.qmp("block_set_io_throttle", conv_keys=False, + **params) +self.do_check_throttle_node(expect=False) +self.vm.shutdown() + +def do_test_job(self, cmd, **args): +params = {"device": "drive0", + "bps": 1024, + "bps_rd": 0, + "bps_wr": 0, + "iops": 0, + "iops_rd": 0, + "iops_wr": 0, + } +result = self.vm.qmp("block_set_io_throttle", conv_keys=False, + **params) +self.assert_qmp(result, "return", {}) +result = self.vm.qmp(cmd, **args) +self.assert_qmp(result, "return", {}) +result = self.vm.qmp("query-block-jobs") +self.assert_qmp(result, "return[0]/device", "drive0") + +def do_check_throttle_node(self, expect): +result = self.vm.qmp("query-named-block-nodes") +for r in result["return"]: +if r["drv"] == "throttle": +self.assertTrue(expect) +return +if expect: +""" throttle_node missing! """ +self.assertTrue(False) + +def do_check_params(self, file): +result = self.vm.qmp("query-block") +self.assert_qmp(result, "return[0]/inserted/bps", 1024) +self.assert_qmp(result, "return[0]/inserted/drv", iotests.imgfmt) +self.assert_qmp(result, "return[0]/inserted/file", file) + +""" +Check that query-block reports the correct throttling parameters while +ignoring the implicit throttle node. +""" +def test_query_block(self): +params = {"device": "drive0", + "bps": 1024, + "bps_rd": 0, + "bps_wr": 0, + "iops":
[Qemu-devel] [PATCH v3 2/7] block: add options parameter to bdrv_new_open_driver()
Allow passing a QDict *options parameter to bdrv_new_open_driver() so that it can be used if a driver needs it upon creation. The previous behaviour (empty bs->options and bs->explicit_options) remains when options is NULL. Reviewed-by: Alberto Garcia <be...@igalia.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/block.h | 2 +- block.c | 16 +--- block/commit.c| 4 ++-- block/mirror.c| 2 +- block/vvfat.c | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/block/block.h b/include/block/block.h index ab80195378..d1f03cb48b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -263,7 +263,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp); BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp); + int flags, QDict *options, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, int flags); diff --git a/block.c b/block.c index e35d546c08..2de1c29eb3 100644 --- a/block.c +++ b/block.c @@ -1153,16 +1153,26 @@ open_failed: return ret; } +/* + * If options is not NULL, its ownership is transferred to the block layer. The + * caller must use QINCREF() if they wish to keep ownership. + */ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp) + int flags, QDict *options, Error **errp) { BlockDriverState *bs; int ret; bs = bdrv_new(); bs->open_flags = flags; -bs->explicit_options = qdict_new(); -bs->options = qdict_new(); +if (options) { +bs->explicit_options = qdict_clone_shallow(options); +bs->options = qdict_clone_shallow(options); +QDECREF(options); +} else { +bs->explicit_options = qdict_new(); +bs->options = qdict_new(); +} bs->opaque = NULL; update_options_from_flags(bs->options, flags); diff --git a/block/commit.c b/block/commit.c index c7857c3321..539e23c3f8 100644 --- a/block/commit.c +++ b/block/commit.c @@ -342,7 +342,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, /* Insert commit_top block node above top, so we can block consistent read * on the backing chain below it */ commit_top_bs = bdrv_new_open_driver(_commit_top, filter_node_name, 0, - errp); + NULL, errp); if (commit_top_bs == NULL) { goto fail; } @@ -494,7 +494,7 @@ int bdrv_commit(BlockDriverState *bs) backing_file_bs = backing_bs(bs); commit_top_bs = bdrv_new_open_driver(_commit_top, NULL, BDRV_O_RDWR, - _err); + NULL, _err); if (commit_top_bs == NULL) { error_report_err(local_err); goto ro_cleanup; diff --git a/block/mirror.c b/block/mirror.c index c9a6a3ca86..e1a160e6ea 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1164,7 +1164,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, * reads on the top, while disabling it in the intermediate nodes, and make * the backing chain writable. */ mirror_top_bs = bdrv_new_open_driver(_mirror_top, filter_node_name, - BDRV_O_RDWR, errp); + BDRV_O_RDWR, NULL, errp); if (mirror_top_bs == NULL) { return; } diff --git a/block/vvfat.c b/block/vvfat.c index a9e207f7f0..6c59473baf 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3194,7 +3194,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) #endif backing = bdrv_new_open_driver(_write_target, NULL, BDRV_O_ALLOW_RDWR, - _abort); + NULL, _abort); *(void**) backing->opaque = s; bdrv_set_backing_hd(s->bs, backing, _abort); -- 2.11.0
[Qemu-devel] [PATCH v9 5/6] block: add throttle block filter driver
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 <be...@igalia.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- 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
[Qemu-devel] [PATCH v9 1/6] block: move ThrottleGroup membership to ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 39 +- include/sysemu/block-backend.h | 20 +-- block/block-backend.c | 66 + block/qapi.c| 8 +- block/throttle-groups.c | 288 blockdev.c | 4 +- tests/test-throttle.c | 53 7 files changed, 252 insertions(+), 226 deletions(-) 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/block/block-backend.c b/block/block-backend.c index e9798e897d..7b981e00bb 100644 --- a/block/block
[Qemu-devel] [PATCH v9 6/6] qemu-iotests: add 184 for throttle filter driver
Reviewed-by: Alberto Garcia <be...@igalia.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/184 | 205 +++ tests/qemu-iotests/184.out | 300 + tests/qemu-iotests/group | 1 + 3 files changed, 506 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 00..704f38f936 --- /dev/null +++ b/tests/qemu-iotests/184 @@ -0,0 +1,205 @@ +#!/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 qcow2 +_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 <
[Qemu-devel] [PATCH v9 0/6] add throttle block driver filter
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) v9: fix suggestions by stefanha in patch 4 add 'throttle_group_can_be_deleted' function in patch 4 v8: fix suggestions by berto v7: remove anonymous groups error on missing throttle-group option on block/throttle.c change switch case format in block/throttle-groups.c (berto) v6: don't allow named group limit configuration in block/throttle.c; allow either -drive driver=throttle,file.filename=foo.qcow2, \ limits.iops-total=... or -drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar fixes suggested by berto v5: fix crash in 'add aio_context field in ThrottleGroupMember' fix suggestions in block/throttle.c v4: fix suggestions in block/throttle.c fix suggestions in block/throttle_groups.c add doc note in BlockDevOptionsThrottle 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 (6): 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 qemu-iotests: add 184 for throttle filter driver qapi/block-core.json| 66 +++- include/block/throttle-groups.h | 52 ++- include/qemu/throttle-options.h | 60 +++- include/qemu/throttle.h | 3 + include/sysemu/block-backend.h | 20 +- block/block-backend.c | 62 ++-- block/qapi.c| 8 +- block/throttle-groups.c | 748 ++-- block/throttle.c| 250 ++ blockdev.c | 4 +- tests/test-throttle.c | 111 +++--- util/throttle.c | 151 block/Makefile.objs | 1 + tests/qemu-iotests/184 | 205 +++ tests/qemu-iotests/184.out | 300 tests/qemu-iotests/group| 1 + 16 files changed, 1721 insertions(+), 321 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
[Qemu-devel] [PATCH v9 4/6] block: convert ThrottleGroup to object with QOM
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. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- qapi/block-core.json| 48 + include/block/throttle-groups.h | 3 + include/qemu/throttle-options.h | 59 -- include/qemu/throttle.h | 3 + block/throttle-groups.c | 422 tests/test-throttle.c | 1 + util/throttle.c | 151 ++ 7 files changed, 627 insertions(+), 60 deletions(-) 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', +'*iop
[Qemu-devel] [PATCH v9 2/6] block: add aio_context field in ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 7 - block/block-backend.c | 15 -- block/throttle-groups.c | 38 - tests/test-throttle.c | 63 + 4 files changed, 69 insertions(+), 54 deletions(-) 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/block/block-backend.c b/block/block-backend.c index 7b981e00bb..9ba800e733 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1726,18 +1726,14 @@ 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 = >public.throttle_group_member; if (bs) { -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_detach_aio_context(tt); +if (tgm->throttle_state) { +throttle_group_detach_aio_context(tgm); +throttle_group_attach_aio_context(tgm, new_context); } bdrv_set_aio_context(bs, new_context); -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_attach_aio_context(tt, new_context); -} } } @@ -1970,7 +1966,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(>public.throttle_group_member, group); +throttle_group_register_tgm(>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..3b07b25f39 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, ); -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 (rea
[Qemu-devel] [PATCH v9 3/6] block: tidy ThrottleGroupMember initializations
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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 9ba800e733..c51fb8c8aa 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -252,9 +252,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(>public.throttle_group_member.throttled_reqs_lock); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>remove_bs_notifiers); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 3b07b25f39..7749cf043f 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(>throttled_reqs_lock); +qemu_co_queue_init(>throttled_reqs[0]); +qemu_co_queue_init(>throttled_reqs[1]); qemu_mutex_unlock(>lock); } -- 2.11.0
Re: [Qemu-devel] [PATCH v7 6/6] qemu-iotests: add 184 for throttle filter driver
On Thu, Aug 24, 2017 at 07:43:08PM +0100, Stefan Hajnoczi wrote: On Tue, Aug 22, 2017 at 01:15:35PM +0300, Manos Pitsidianakis wrote: +_supported_fmt qcow2 What makes this test qcow2-specific? With additional filtering for IMGFMT it should be possible to run this on any image format. I think none of the available filters hide all of the image details in query-block and company. _filter_imgfmt hides the image extensions but not the image specific fields (eg virtual-size, actual-size and format-specific). Perhaps a new filter that hides everything but "format" and "filename" in the "image" subdict can be introduced later. signature.asc Description: PGP signature
[Qemu-devel] [PATCH v8 6/6] qemu-iotests: add 184 for throttle filter driver
Reviewed-by: Alberto Garcia <be...@igalia.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/184 | 205 +++ tests/qemu-iotests/184.out | 300 + tests/qemu-iotests/group | 1 + 3 files changed, 506 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 00..704f38f936 --- /dev/null +++ b/tests/qemu-iotests/184 @@ -0,0 +1,205 @@ +#!/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 qcow2 +_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 <
[Qemu-devel] [PATCH v8 0/6] add throttle block driver filter
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) v8: fix suggestions by berto v7: remove anonymous groups error on missing throttle-group option on block/throttle.c change switch case format in block/throttle-groups.c (berto) v6: don't allow named group limit configuration in block/throttle.c; allow either -drive driver=throttle,file.filename=foo.qcow2, \ limits.iops-total=... or -drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar fixes suggested by berto v5: fix crash in 'add aio_context field in ThrottleGroupMember' fix suggestions in block/throttle.c v4: fix suggestions in block/throttle.c fix suggestions in block/throttle_groups.c add doc note in BlockDevOptionsThrottle 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 (6): 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 qemu-iotests: add 184 for throttle filter driver qapi/block-core.json| 66 +++- include/block/throttle-groups.h | 48 ++- include/qemu/throttle-options.h | 60 ++-- include/qemu/throttle.h | 3 + include/sysemu/block-backend.h | 20 +- block/block-backend.c | 62 ++-- block/qapi.c| 8 +- block/throttle-groups.c | 736 +--- block/throttle.c| 250 ++ blockdev.c | 4 +- tests/test-throttle.c | 111 +++--- util/throttle.c | 151 + block/Makefile.objs | 1 + tests/qemu-iotests/184 | 205 +++ tests/qemu-iotests/184.out | 300 tests/qemu-iotests/group| 1 + 16 files changed, 1705 insertions(+), 321 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
[Qemu-devel] [PATCH v8 4/6] block: convert ThrottleGroup to object with QOM
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. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- qapi/block-core.json| 48 + include/block/throttle-groups.h | 3 + include/qemu/throttle-options.h | 59 -- include/qemu/throttle.h | 3 + block/throttle-groups.c | 413 tests/test-throttle.c | 1 + util/throttle.c | 151 +++ 7 files changed, 618 insertions(+), 60 deletions(-) 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', +
[Qemu-devel] [PATCH v8 5/6] block: add throttle block filter driver
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. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- qapi/block-core.json| 18 ++- include/block/throttle-groups.h | 1 + include/qemu/throttle-options.h | 1 + block/throttle-groups.c | 12 +- block/throttle.c| 250 block/Makefile.objs | 1 + 6 files changed, 281 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..ec969e84fe 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -76,5 +76,6 @@ 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); +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 8421921bed..98ec39df3f 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -101,6 +101,11 @@ static ThrottleGroup *throttle_group_by_name(const char *name) return NULL; } +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 +548,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 +719,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)) { error_setg(errp, "A group with this name already exists"); return; } diff --git a/block/throttle.c
[Qemu-devel] [PATCH v8 2/6] block: add aio_context field in ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 7 - block/block-backend.c | 15 -- block/throttle-groups.c | 38 - tests/test-throttle.c | 63 + 4 files changed, 69 insertions(+), 54 deletions(-) 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/block/block-backend.c b/block/block-backend.c index 7b981e00bb..9ba800e733 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1726,18 +1726,14 @@ 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 = >public.throttle_group_member; if (bs) { -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_detach_aio_context(tt); +if (tgm->throttle_state) { +throttle_group_detach_aio_context(tgm); +throttle_group_attach_aio_context(tgm, new_context); } bdrv_set_aio_context(bs, new_context); -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_attach_aio_context(tt, new_context); -} } } @@ -1970,7 +1966,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(>public.throttle_group_member, group); +throttle_group_register_tgm(>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..3b07b25f39 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, ); -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 (rea
[Qemu-devel] [PATCH v8 1/6] block: move ThrottleGroup membership to ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 39 +- include/sysemu/block-backend.h | 20 +-- block/block-backend.c | 66 + block/qapi.c| 8 +- block/throttle-groups.c | 288 blockdev.c | 4 +- tests/test-throttle.c | 53 7 files changed, 252 insertions(+), 226 deletions(-) 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/block/block-backend.c b/block/block-backend.c index e9798e897d..7b981e00bb 100644 --- a/block/block
[Qemu-devel] [PATCH v8 3/6] block: tidy ThrottleGroupMember initializations
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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 9ba800e733..c51fb8c8aa 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -252,9 +252,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(>public.throttle_group_member.throttled_reqs_lock); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>remove_bs_notifiers); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 3b07b25f39..7749cf043f 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(>throttled_reqs_lock); +qemu_co_queue_init(>throttled_reqs[0]); +qemu_co_queue_init(>throttled_reqs[1]); qemu_mutex_unlock(>lock); } -- 2.11.0
Re: [Qemu-devel] [PATCH v7 5/6] block: add throttle block filter driver
On Tue, Aug 22, 2017 at 04:16:26PM +0200, Alberto Garcia wrote: On Tue 22 Aug 2017 12:15:34 PM CEST, Manos Pitsidianakis wrote: @@ -548,6 +548,11 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) ThrottleGroupMember *token; int i; +if (!ts) { +/* Discard uninitialized tgm */ +return; +} + Is this change needed in case throttle_configure_tgm() fails? Yes, now all errors are before throttle_group_register_tgm(), therefore the unregister part in throttle_close() will not have valid data. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v7 4/6] block: convert ThrottleGroup to object with QOM
On Tue, Aug 22, 2017 at 03:25:41PM +0200, Alberto Garcia wrote: +/* This function edits throttle_groups and must be called under the global + * mutex */ +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp) +{ +ThrottleGroup *tg = THROTTLE_GROUP(obj); +ThrottleConfig 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)); +} +/* We must have a group name at this point */ +assert(tg->name); + +/* error if name is duplicate */ +if (throttle_group_exists(tg->name)) { +error_setg(errp, "A group with this name already exists"); +return; +} + +/* check validity */ +throttle_get_config(>ts, ); +if (!throttle_is_valid(, errp)) { +return; +} My fault here because I suggested its removal, but I actually think we still need to call throttle_config() here so bkt->max is initialized in throttle_fix_bucket(). I'll take care of updating this call once my patches to remove throttle_fix_bucket() are applied, but for now we still need it. Oh yes, that is the reason I originally had done it but forgot why. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v2 3/6] block: require job-id when device is a node name
On Tue, Aug 22, 2017 at 11:57:28AM +0200, Alberto Garcia wrote: On Mon 21 Aug 2017 05:39:48 PM CEST, Manos Pitsidianakis wrote: -if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) { -job_id = bdrv_get_device_name(bs); -if (!*job_id) { -error_setg(errp, "An explicit job ID is required for this node"); -return NULL; -} -} - -if (job_id) { -if (flags & BLOCK_JOB_INTERNAL) { +if (flags & BLOCK_JOB_INTERNAL) { +if (job_id) { error_setg(errp, "Cannot specify job ID for internal block job"); return NULL; } When BLOCK_JOB_INTERNAL is set, then job_id must be NULL... - +} else { +/* Require job-id. */ +assert(job_id); if (!id_wellformed(job_id)) { error_setg(errp, "Invalid job ID '%s'", job_id); return NULL; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index f13ad05c0d..ff906808a6 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -112,8 +112,7 @@ struct BlockJobDriver { /** * block_job_create: - * @job_id: The id of the newly-created job, or %NULL to have one - * generated automatically. + * @job_id: The id of the newly-created job, must be non %NULL. ...but here you say that it must not be NULL. And since job_id can be NULL in some cases I think I'd replace the assert(job_id) that you added to block_job_create() with a normal pointer check + error_setg(). @@ -93,9 +93,6 @@ static void test_job_ids(void) blk[1] = create_blk("drive1"); blk[2] = create_blk("drive2"); -/* No job ID provided and the block backend has no name */ -job[0] = do_test_id(blk[0], NULL, false); - If you decide to handle NULL ids by returning and error instead of asserting, we should keep a couple of tests for that scenario. Berto Thanks, I will change that. What cases are you thinking of besides internal jobs though? Both cases (external job with a NULL id and internal job with non-NULL id), checking that block_job_create() does return an error. I thought we handled the external job case by requiring job_id is set before calling block_job_create(). I will check my patch again. And should documentation of block_job_create reflect that internal jobs have job_id == NULL? Yes, it should document the restrictions of the job_id parameter. Berto signature.asc Description: PGP signature
[Qemu-devel] [PATCH v7 4/6] block: convert ThrottleGroup to object with QOM
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. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- qapi/block-core.json| 48 + include/block/throttle-groups.h | 3 + include/qemu/throttle-options.h | 59 -- include/qemu/throttle.h | 3 + block/throttle-groups.c | 419 tests/test-throttle.c | 1 + util/throttle.c | 151 +++ 7 files changed, 624 insertions(+), 60 deletions(-) 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', +
[Qemu-devel] [PATCH v7 5/6] block: add throttle block filter driver
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. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- qapi/block-core.json| 18 ++- include/block/throttle-groups.h | 1 + include/qemu/throttle-options.h | 1 + block/throttle-groups.c | 7 +- block/throttle.c| 250 block/Makefile.objs | 1 + 6 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 block/throttle.c diff --git a/qapi/block-core.json b/qapi/block-core.json index 0bdc69aa5f..405c181954 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..ec969e84fe 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -76,5 +76,6 @@ 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); +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 a4268a954e..4b483a16d4 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -101,7 +101,7 @@ static ThrottleGroup *throttle_group_by_name(const char *name) return NULL; } -static bool throttle_group_exists(const char *name) +bool throttle_group_exists(const char *name) { return throttle_group_by_name(name) ? true : false; } @@ -548,6 +548,11 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) ThrottleGroupMember *token; int i; +if (!ts) { +/* Discard uninitialized 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])); diff --git a/block/throttle.c b/block/throttle.c new file mode 100644 index 00..910de27dcd --- /dev/null +++ b/block/throttle.c @@ -0,0 +1,250 @@ +/* + * 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 optio
[Qemu-devel] [PATCH v7 2/6] block: add aio_context field in ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 7 - block/block-backend.c | 15 -- block/throttle-groups.c | 38 - tests/test-throttle.c | 63 + 4 files changed, 69 insertions(+), 54 deletions(-) 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/block/block-backend.c b/block/block-backend.c index 7b981e00bb..9ba800e733 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1726,18 +1726,14 @@ 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 = >public.throttle_group_member; if (bs) { -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_detach_aio_context(tt); +if (tgm->throttle_state) { +throttle_group_detach_aio_context(tgm); +throttle_group_attach_aio_context(tgm, new_context); } bdrv_set_aio_context(bs, new_context); -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_attach_aio_context(tt, new_context); -} } } @@ -1970,7 +1966,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(>public.throttle_group_member, group); +throttle_group_register_tgm(>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..3b07b25f39 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, ); -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 (rea
[Qemu-devel] [PATCH v7 6/6] qemu-iotests: add 184 for throttle filter driver
Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/184 | 205 +++ tests/qemu-iotests/184.out | 300 + tests/qemu-iotests/group | 1 + 3 files changed, 506 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 00..704f38f936 --- /dev/null +++ b/tests/qemu-iotests/184 @@ -0,0 +1,205 @@ +#!/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 qcow2 +_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 <
[Qemu-devel] [PATCH v7 3/6] block: tidy ThrottleGroupMember initializations
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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 9ba800e733..c51fb8c8aa 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -252,9 +252,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(>public.throttle_group_member.throttled_reqs_lock); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>remove_bs_notifiers); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 3b07b25f39..7749cf043f 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(>throttled_reqs_lock); +qemu_co_queue_init(>throttled_reqs[0]); +qemu_co_queue_init(>throttled_reqs[1]); qemu_mutex_unlock(>lock); } -- 2.11.0
[Qemu-devel] [PATCH v7 1/6] block: move ThrottleGroup membership to ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 39 +- include/sysemu/block-backend.h | 20 +-- block/block-backend.c | 66 + block/qapi.c| 8 +- block/throttle-groups.c | 288 blockdev.c | 4 +- tests/test-throttle.c | 53 7 files changed, 252 insertions(+), 226 deletions(-) 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/block/block-backend.c b/block/block-backend.c index e9798e897d..7b981e00bb 100644 --- a/block/block
[Qemu-devel] [PATCH v7 0/6] add throttle block driver filter
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) v7: remove anonymous groups error on missing throttle-group option on block/throttle.c change switch case format in block/throttle-groups.c (berto) v6: don't allow named group limit configuration in block/throttle.c; allow either -drive driver=throttle,file.filename=foo.qcow2, \ limits.iops-total=... or -drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar fixes suggested by berto v5: fix crash in 'add aio_context field in ThrottleGroupMember' fix suggestions in block/throttle.c v4: fix suggestions in block/throttle.c fix suggestions in block/throttle_groups.c add doc note in BlockDevOptionsThrottle 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 (6): 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 qemu-iotests: add 184 for throttle filter driver qapi/block-core.json| 66 +++- include/block/throttle-groups.h | 48 ++- include/qemu/throttle-options.h | 60 ++-- include/qemu/throttle.h | 3 + include/sysemu/block-backend.h | 20 +- block/block-backend.c | 62 ++-- block/qapi.c| 8 +- block/throttle-groups.c | 737 +--- block/throttle.c| 250 ++ blockdev.c | 4 +- tests/test-throttle.c | 111 +++--- util/throttle.c | 151 block/Makefile.objs | 1 + tests/qemu-iotests/184 | 205 +++ tests/qemu-iotests/184.out | 300 tests/qemu-iotests/group| 1 + 16 files changed, 1706 insertions(+), 321 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
Re: [Qemu-devel] [PATCH v2 3/6] block: require job-id when device is a node name
On Mon, Aug 21, 2017 at 05:05:30PM +0200, Alberto Garcia wrote: On Wed 09 Aug 2017 04:02:53 PM CEST, Manos Pitsidianakis wrote: @@ -622,20 +622,14 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } -if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) { -job_id = bdrv_get_device_name(bs); -if (!*job_id) { -error_setg(errp, "An explicit job ID is required for this node"); -return NULL; -} -} - -if (job_id) { -if (flags & BLOCK_JOB_INTERNAL) { +if (flags & BLOCK_JOB_INTERNAL) { +if (job_id) { error_setg(errp, "Cannot specify job ID for internal block job"); return NULL; } When BLOCK_JOB_INTERNAL is set, then job_id must be NULL... - +} else { +/* Require job-id. */ +assert(job_id); if (!id_wellformed(job_id)) { error_setg(errp, "Invalid job ID '%s'", job_id); return NULL; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index f13ad05c0d..ff906808a6 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -112,8 +112,7 @@ struct BlockJobDriver { /** * block_job_create: - * @job_id: The id of the newly-created job, or %NULL to have one - * generated automatically. + * @job_id: The id of the newly-created job, must be non %NULL. ...but here you say that it must not be NULL. And since job_id can be NULL in some cases I think I'd replace the assert(job_id) that you added to block_job_create() with a normal pointer check + error_setg(). @@ -93,9 +93,6 @@ static void test_job_ids(void) blk[1] = create_blk("drive1"); blk[2] = create_blk("drive2"); -/* No job ID provided and the block backend has no name */ -job[0] = do_test_id(blk[0], NULL, false); - If you decide to handle NULL ids by returning and error instead of asserting, we should keep a couple of tests for that scenario. Berto Thanks, I will change that. What cases are you thinking of besides internal jobs though? And should documentation of block_job_create reflect that internal jobs have job_id == NULL? signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH 3/4] throttle: Remove throttle_fix_bucket() / throttle_unfix_bucket()
On Mon, Aug 21, 2017 at 02:10:59PM +0200, Alberto Garcia wrote: On Sat 19 Aug 2017 05:23:12 PM CEST, Manos Pitsidianakis wrote: -/* If the bucket is full then we have to wait */ -extra = bkt->level - bkt->max * bkt->burst_length; +if (!bkt->max) { +/* If bkt->max is 0 we still want to allow short bursts of I/O + * from the guest, otherwise every other request will be throttled + * and performance will suffer considerably. */ +bucket_size = bkt->avg / 10; +burst_bucket_size = 0; Might be wrong, but shouldn't that be bucket_size = (bkt->avg / 10) * bkt->burst_length? burst_bucket_size = (bkt->avg / 10) / 10; if !bkt->max then the user didn't define any bursts, and the I/O is only throttled to the rate set in bkt->avg. +} else { +/* If we have a burst limit then we have to wait until all I/O + * at burst rate has finished before throttling to bkt->avg */ +bucket_size = bkt->max * bkt->burst_length; +burst_bucket_size = bkt->max / 10; +} + +/* If the main bucket is full then we have to wait */ +extra = bkt->level - bucket_size; because it used to be that if bkt->max is 0 then extra = bkt->level - (bkt->avg /10) * bkt->burst_length; //fixed value and now it's extra = bkt->level - (bkt->avg / 10); and if (extra > 0) { return throttle_do_compute_wait(bkt->avg, extra); } -/* If the bucket is not full yet we have to make sure that we - * fulfill the goal of bkt->max units per second. */ +/* If the main bucket is not full yet we still have to check the + * burst bucket in order to enforce the burst limit */ if (bkt->burst_length > 1) { -/* We use 1/10 of the max value to smooth the throttling. - * See throttle_fix_bucket() for more details. */ -extra = bkt->burst_level - bkt->max / 10; +extra = bkt->burst_level - burst_bucket_size; This used to be extra = bkt->burst_level - (bkt->avg / 10) / 10; and now it's extra = bkt->burst_level - 0; No, if bkt->burst_length > 1 then bkt->max != 0, and therefore burst_bucket_size is never 0 either. Perhaps you missed the ! in the first "if (!bkt->max)" ? Now it makes sense, thanks :) I reread the patch and you can add Reviewed-by: Manos Pitsidianakis <el13...@mail.ntua.gr> signature.asc Description: PGP signature
[Qemu-devel] [PATCH v6 3/6] block: tidy ThrottleGroupMember initializations
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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 9ba800e733..c51fb8c8aa 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -252,9 +252,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(>public.throttle_group_member.throttled_reqs_lock); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>remove_bs_notifiers); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 3b07b25f39..7749cf043f 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(>throttled_reqs_lock); +qemu_co_queue_init(>throttled_reqs[0]); +qemu_co_queue_init(>throttled_reqs[1]); qemu_mutex_unlock(>lock); } -- 2.11.0
[Qemu-devel] [PATCH v6 1/6] block: move ThrottleGroup membership to ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 39 +- include/sysemu/block-backend.h | 20 +-- block/block-backend.c | 66 + block/qapi.c| 8 +- block/throttle-groups.c | 288 blockdev.c | 4 +- tests/test-throttle.c | 53 7 files changed, 252 insertions(+), 226 deletions(-) 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/block/block-backend.c b/block/block-backend.c index e9798e897d..7b981e00bb 100644 --- a/block/block
[Qemu-devel] [PATCH v6 4/6] block: convert ThrottleGroup to object with QOM
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 <el13...@mail.ntua.gr> --- qapi/block-core.json| 48 + include/block/throttle-groups.h | 3 + include/qemu/throttle-options.h | 59 -- include/qemu/throttle.h | 3 + block/throttle-groups.c | 416 tests/test-throttle.c | 1 + util/throttle.c | 151 +++ 7 files changed, 621 insertions(+), 60 deletions(-) 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',
[Qemu-devel] [PATCH v6 5/6] block: add throttle block filter driver
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=... which creates an anonymous group with the specified limits, or -drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar which registers the throttle filter node with the ThrottleGroup bar. The given groups must be created with object-add or -object. The limit parameters and their semantics are identical to the hardcoded throttling ones. limits.* cannot be specified at the same time as throttle-group. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- qapi/block-core.json| 22 ++- include/qapi/qmp/qdict.h| 1 + include/qemu/throttle-options.h | 1 + block/throttle-groups.c | 5 + block/throttle.c| 332 qobject/qdict.c | 2 +- block/Makefile.objs | 1 + 7 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 block/throttle.c diff --git a/qapi/block-core.json b/qapi/block-core.json index 0bdc69aa5f..12fd749a94 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,24 @@ '*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. If not specified, an +#anonymous group will be created, which cannot be +#referenced by other throttle nodes. +# @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 +3174,7 @@ 'replication':'BlockdevOptionsReplication', 'sheepdog': 'BlockdevOptionsSheepdog', 'ssh':'BlockdevOptionsSsh', + 'throttle': 'BlockdevOptionsThrottle', 'vdi':'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat', diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 363e431106..c1492ba84a 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -80,6 +80,7 @@ QDict *qdict_clone_shallow(const QDict *src); void qdict_flatten(QDict *qdict); void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); +int qdict_count_prefixed_entries(const QDict *src, const char *start); void qdict_array_split(QDict *src, QList **dst); int qdict_array_entries(QDict *src, const char *subqdict); QObject *qdict_crumple(const QDict *src, Error **errp); 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 15f1697671..3d81e1ac27 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -534,6 +534,11 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) ThrottleGroupMember *token; int i; +if (!ts) { +/* Discard uninitialized 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])); diff --git a/block/throttle.c b/block/throttle.c new file mode 100644 index 00..daf4af4970 --- /dev/null +++ b/bl
[Qemu-devel] [PATCH v6 6/6] qemu-iotests: add 184 for throttle filter driver
Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/184 | 244 tests/qemu-iotests/184.out | 339 + tests/qemu-iotests/group | 1 + 3 files changed, 584 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 00..7376c5119b --- /dev/null +++ b/tests/qemu-iotests/184 @@ -0,0 +1,244 @@ +#!/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 qcow2 +_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 <
[Qemu-devel] [PATCH v6 2/6] block: add aio_context field in ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 7 - block/block-backend.c | 15 -- block/throttle-groups.c | 38 - tests/test-throttle.c | 63 + 4 files changed, 69 insertions(+), 54 deletions(-) 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/block/block-backend.c b/block/block-backend.c index 7b981e00bb..9ba800e733 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1726,18 +1726,14 @@ 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 = >public.throttle_group_member; if (bs) { -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_detach_aio_context(tt); +if (tgm->throttle_state) { +throttle_group_detach_aio_context(tgm); +throttle_group_attach_aio_context(tgm, new_context); } bdrv_set_aio_context(bs, new_context); -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_attach_aio_context(tt, new_context); -} } } @@ -1970,7 +1966,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(>public.throttle_group_member, group); +throttle_group_register_tgm(>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..3b07b25f39 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, ); -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 (rea
[Qemu-devel] [PATCH v6 0/6] add throttle block driver filter
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) v6: don't allow named group limit configuration in block/throttle.c; allow either -drive driver=throttle,file.filename=foo.qcow2, \ limits.iops-total=... or -drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar fixes suggested by berto v5: fix crash in 'add aio_context field in ThrottleGroupMember' fix suggestions in block/throttle.c v4: fix suggestions in block/throttle.c fix suggestions in block/throttle_groups.c add doc note in BlockDevOptionsThrottle 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 (6): 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 qemu-iotests: add 184 for throttle filter driver qapi/block-core.json| 70 +++- include/block/throttle-groups.h | 47 ++- include/qapi/qmp/qdict.h| 1 + include/qemu/throttle-options.h | 60 ++-- include/qemu/throttle.h | 3 + include/sysemu/block-backend.h | 20 +- block/block-backend.c | 62 ++-- block/qapi.c| 8 +- block/throttle-groups.c | 734 +--- block/throttle.c| 332 ++ blockdev.c | 4 +- qobject/qdict.c | 2 +- tests/test-throttle.c | 111 +++--- util/throttle.c | 151 + block/Makefile.objs | 1 + tests/qemu-iotests/184 | 244 + tests/qemu-iotests/184.out | 339 +++ tests/qemu-iotests/group| 1 + 18 files changed, 1868 insertions(+), 322 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
Re: [Qemu-devel] [PATCH 3/4] throttle: Remove throttle_fix_bucket() / throttle_unfix_bucket()
On Thu, Aug 17, 2017 at 05:28:14PM +0300, Alberto Garcia wrote: The throttling code can change internally the value of bkt->max if it hasn't been set by the user. The problem with this is that if we want to retrieve the original value we have to undo this change first. This is ugly and unnecessary: this patch removes the throttle_fix_bucket() and throttle_unfix_bucket() functions completely and moves the logic to throttle_compute_wait(). Signed-off-by: Alberto Garcia--- util/throttle.c | 62 + 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/util/throttle.c b/util/throttle.c index 9a6bda813c..1f29cf9918 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -95,23 +95,36 @@ static int64_t throttle_do_compute_wait(double limit, double extra) int64_t throttle_compute_wait(LeakyBucket *bkt) { double extra; /* the number of extra units blocking the io */ +double bucket_size; /* I/O before throttling to bkt->avg */ +double burst_bucket_size; /* Before throttling to bkt->max */ if (!bkt->avg) { return 0; } -/* If the bucket is full then we have to wait */ -extra = bkt->level - bkt->max * bkt->burst_length; +if (!bkt->max) { +/* If bkt->max is 0 we still want to allow short bursts of I/O + * from the guest, otherwise every other request will be throttled + * and performance will suffer considerably. */ +bucket_size = bkt->avg / 10; +burst_bucket_size = 0; Might be wrong, but shouldn't that be bucket_size = (bkt->avg / 10) * bkt->burst_length? burst_bucket_size = (bkt->avg / 10) / 10; +} else { +/* If we have a burst limit then we have to wait until all I/O + * at burst rate has finished before throttling to bkt->avg */ +bucket_size = bkt->max * bkt->burst_length; +burst_bucket_size = bkt->max / 10; +} + +/* If the main bucket is full then we have to wait */ +extra = bkt->level - bucket_size; because it used to be that if bkt->max is 0 then extra = bkt->level - (bkt->avg /10) * bkt->burst_length; //fixed value and now it's extra = bkt->level - (bkt->avg / 10); and if (extra > 0) { return throttle_do_compute_wait(bkt->avg, extra); } -/* If the bucket is not full yet we have to make sure that we - * fulfill the goal of bkt->max units per second. */ +/* If the main bucket is not full yet we still have to check the + * burst bucket in order to enforce the burst limit */ if (bkt->burst_length > 1) { -/* We use 1/10 of the max value to smooth the throttling. - * See throttle_fix_bucket() for more details. */ -extra = bkt->burst_level - bkt->max / 10; +extra = bkt->burst_level - burst_bucket_size; This used to be extra = bkt->burst_level - (bkt->avg / 10) / 10; and now it's extra = bkt->burst_level - 0; if (extra > 0) { return throttle_do_compute_wait(bkt->max, extra); } @@ -358,31 +371,6 @@ bool throttle_is_valid(ThrottleConfig *cfg, Error **errp) return true; } -/* fix bucket parameters */ -static void throttle_fix_bucket(LeakyBucket *bkt) -{ -double min; - -/* zero bucket level */ -bkt->level = bkt->burst_level = 0; - -/* If bkt->max is 0 we still want to allow short bursts of I/O - * from the guest, otherwise every other request will be throttled - * and performance will suffer considerably. */ -min = bkt->avg / 10; -if (bkt->avg && !bkt->max) { -bkt->max = min; -} -} - -/* undo internal bucket parameter changes (see throttle_fix_bucket()) */ -static void throttle_unfix_bucket(LeakyBucket *bkt) -{ -if (bkt->max < bkt->avg) { -bkt->max = 0; -} -} - /* Used to configure the throttle * * @ts: the throttle state we are working on @@ -397,8 +385,10 @@ void throttle_config(ThrottleState *ts, ts->cfg = *cfg; +/* Zero bucket level */ for (i = 0; i < BUCKETS_COUNT; i++) { -throttle_fix_bucket(>cfg.buckets[i]); +ts->cfg.buckets[i].level = 0; +ts->cfg.buckets[i].burst_level = 0; } ts->previous_leak = qemu_clock_get_ns(clock_type); @@ -411,13 +401,7 @@ void throttle_config(ThrottleState *ts, */ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg) { -int i; - *cfg = ts->cfg; - -for (i = 0; i < BUCKETS_COUNT; i++) { -throttle_unfix_bucket(>buckets[i]); -} } -- 2.11.0 signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH 4/4] throttle: Make LeakyBucket.avg and LeakyBucket.max integer types
On Thu, Aug 17, 2017 at 05:28:15PM +0300, Alberto Garcia wrote: Both the throttling limits set with the throttling.iops-* and throttling.bps-* options and their QMP equivalents defined in the BlockIOThrottle struct are integer values. Those limits are also reported in the BlockDeviceInfo struct and they are integers there as well. Therefore there's no reason to store them internally as double and do the conversion everytime we're setting or querying them, so this patch uses int64_t for those types. LeakyBucket.level and LeakyBucket.burst_level do however remain double because their value changes depending on the fraction of time elapsed since the previous I/O operation. Signed-off-by: Alberto Garcia <be...@igalia.com> Reviewed-by: Manos Pitsidianakis <el13...@mail.ntua.gr> include/qemu/throttle.h | 4 ++-- util/throttle.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h index 66a8ac10a4..c466f6ccaa 100644 --- a/include/qemu/throttle.h +++ b/include/qemu/throttle.h @@ -77,8 +77,8 @@ typedef enum { */ typedef struct LeakyBucket { -double avg; /* average goal in units per second */ -double max; /* leaky bucket max burst in units */ +int64_t avg; /* average goal in units per second */ +int64_t max; /* leaky bucket max burst in units */ double level;/* bucket level in units */ double burst_level; /* bucket level in units (for computing bursts) */ unsigned burst_length;/* max length of the burst period, in seconds */ diff --git a/util/throttle.c b/util/throttle.c index 1f29cf9918..55a2451a14 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -106,13 +106,13 @@ int64_t throttle_compute_wait(LeakyBucket *bkt) /* If bkt->max is 0 we still want to allow short bursts of I/O * from the guest, otherwise every other request will be throttled * and performance will suffer considerably. */ -bucket_size = bkt->avg / 10; +bucket_size = (double) bkt->avg / 10; burst_bucket_size = 0; } else { /* If we have a burst limit then we have to wait until all I/O * at burst rate has finished before throttling to bkt->avg */ bucket_size = bkt->max * bkt->burst_length; -burst_bucket_size = bkt->max / 10; +burst_bucket_size = (double) bkt->max / 10; } /* If the main bucket is full then we have to wait */ -- 2.11.0 signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH 1/4] throttle: Fix wrong variable name in the header documentation
On Thu, Aug 17, 2017 at 05:28:12PM +0300, Alberto Garcia wrote: The level of the burst bucket is stored in bkt.burst_level, not bkt.burst_lenght. s/lenght/length, otherwise: Reviewed-by: Manos Pitsidianakis <el13...@mail.ntua.gr> Signed-off-by: Alberto Garcia <be...@igalia.com> --- include/qemu/throttle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h index d056008c18..66a8ac10a4 100644 --- a/include/qemu/throttle.h +++ b/include/qemu/throttle.h @@ -63,7 +63,7 @@ typedef enum { * - The bkt.avg rate does not apply until the bucket is full, * allowing the user to do bursts until then. The I/O limit during * bursts is bkt.max. To enforce this limit we keep an additional - * bucket in bkt.burst_length that leaks at a rate of bkt.max units + * bucket in bkt.burst_level that leaks at a rate of bkt.max units * per second. * * - Because of all of the above, the user can perform I/O at a -- 2.11.0 signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v5 4/6] block: convert ThrottleGroup to object with QOM
On Fri, Aug 18, 2017 at 03:05:31PM +0200, Alberto Garcia wrote: On Fri 18 Aug 2017 05:10:17 AM CEST, Manos Pitsidianakis wrote: * If no ThrottleGroup is found with the given name a new one is * created. * - * @name: the name of the ThrottleGroup + * This function edits throttle_groups and must be called under the global + * mutex. + * + * @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) If we're not going to have anonymous groups in the end this patch needs changes (name == NULL is no longer allowed). +/* This function edits throttle_groups and must be called under the global + * mutex */ +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp) +{ +ThrottleGroup *tg = THROTTLE_GROUP(obj), *iter; +ThrottleConfig *cfg = >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, _groups, list) { +if (!g_strcmp0(tg->name, iter->name) && tg != iter) { I'm just nitpicking here :) but if you change this and put 'tg != iter' first you'll save one unnecessary string comparison. Only in the case where tg == iter, otherwise you have one unnecessary pointer comparison for every other group. +error_setg(errp, "A group with this name already exists"); +return; +} +} +} + +/* unfix buckets to check validity */ +throttle_get_config(>ts, cfg); +if (!throttle_is_valid(cfg, errp)) { +return; +} +/* fix buckets again */ +throttle_config(>ts, tg->clock_type, cfg); throttle_get_config(ts, cfg) makes a copy of the existing configuration, but the cfg pointer that you are passing already points to the existing configuration, so in practice you are doing *(ts->cfg) = *(ts->cfg); throttle_unfix_bucket(...); and since you "unfixed" the configuration, then you need undo the whole thing by setting the config again: *(ts->cfg) = *(ts->cfg); throttle_fix_bucket(...); You should declare a local ThrottleConfig variable, copy the config there, and run throttle_is_valid() on that copy. The final throttle_config() call is unnecessary. I figured I didn't have to make an extra copy but this looks bad in retrospect Once the patches I sent yesterday are merged we'll be able to skip the throttle_get_config() call and do throttle_is_valid(>ts.cfg, errp) directly. +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; +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(_err, "Property cannot be set after initialization"); +goto ret; +} + +visit_type_int64(v, name, , _err); +if (local_err) { +goto ret; +} +if (value < 0) { +error_setg(_err, "Property values cannot be negative"); +goto ret; +} + +cfg = tg->ts.cfg; +switch (info->data_type) { +case UINT64: +{ +uint64_t *field = (void *)[info->type] + info->offset; +*field = value; +} +break; +case DOUBLE: +{ +double *field = (void *)[info->type] + info->offset; +*field = value; +} +break; +case UNSIGNED: +{ +if (value > UINT_MAX) { +error_setg(_err, "%s value must be in the" + "range [0, %u]", info->name, UINT_MAX); +goto ret; +} +unsigned *field = (void *)[info->type] + info->offset; +*field = value; +} +} + +tg->ts.cfg = cfg; There's a bit of black magic here :) This offset business is indeed black magic! And university makes you think the world runs on ML and Haskell... you have a user-defined enumeration (UNSIGNED, DOUBLE, UINT64) to identify C types and I'm worried about what happens if the data types of LeakyBucket change, will we be able to detect the problem? Out of the blue I can think of the following alternative: - There's 6 different buckets (we have BucketType listing them) - There's 3 va
Re: [Qemu-devel] [PATCH v5 5/6] block: add throttle block filter driver
On Fri, Aug 18, 2017 at 10:23:09AM +0200, Alberto Garcia wrote: On Fri 18 Aug 2017 05:10:18 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 I had understood that we would get rid of the limits.* options in this driver, or did I get it wrong? Other than that, the rest of the code looks perfect to me. Berto I was going to send a patch after this was merged along with adding ThrottleGroups to the root container, to speed things up. Do you prefer to do this in this patch? The root container patch probably has to go to the 'remove legacy' series since adding it here means the name collision errors introduce error paths in block/block-backend.c that go away in that series, and that'd be a waste of effort. signature.asc Description: PGP signature
[Qemu-devel] [PATCH v5 4/6] block: convert ThrottleGroup to object with QOM
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 <el13...@mail.ntua.gr> --- qapi/block-core.json| 48 + include/block/throttle-groups.h | 3 + include/qemu/throttle-options.h | 59 -- include/qemu/throttle.h | 3 + block/throttle-groups.c | 421 tests/test-throttle.c | 1 + util/throttle.c | 151 ++ 7 files changed, 626 insertions(+), 60 deletions(-) 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',
[Qemu-devel] [PATCH v5 1/6] block: move ThrottleGroup membership to ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 39 +- include/sysemu/block-backend.h | 20 +-- block/block-backend.c | 66 + block/qapi.c| 8 +- block/throttle-groups.c | 288 blockdev.c | 4 +- tests/test-throttle.c | 53 7 files changed, 252 insertions(+), 226 deletions(-) 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/block/block-backend.c b/block/block-backend.c index e9798e897d..7b981e00bb 100644 --- a/block/block
[Qemu-devel] [PATCH v5 2/6] block: add aio_context field in ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- include/block/throttle-groups.h | 7 - block/block-backend.c | 15 -- block/throttle-groups.c | 38 - tests/test-throttle.c | 63 + 4 files changed, 69 insertions(+), 54 deletions(-) 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/block/block-backend.c b/block/block-backend.c index 7b981e00bb..9ba800e733 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1726,18 +1726,14 @@ 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 = >public.throttle_group_member; if (bs) { -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_detach_aio_context(tt); +if (tgm->throttle_state) { +throttle_group_detach_aio_context(tgm); +throttle_group_attach_aio_context(tgm, new_context); } bdrv_set_aio_context(bs, new_context); -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_attach_aio_context(tt, new_context); -} } } @@ -1970,7 +1966,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(>public.throttle_group_member, group); +throttle_group_register_tgm(>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..3b07b25f39 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, ); -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 (rea
[Qemu-devel] [PATCH v5 5/6] block: add throttle block filter driver
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 <el13...@mail.ntua.gr> --- qapi/block-core.json| 22 ++- include/qemu/throttle-options.h | 1 + block/throttle.c| 314 block/Makefile.objs | 1 + 4 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 block/throttle.c diff --git a/qapi/block-core.json b/qapi/block-core.json index 0bdc69aa5f..12fd749a94 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,24 @@ '*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. If not specified, an +#anonymous group will be created, which cannot be +#referenced by other throttle nodes. +# @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 +3174,7 @@ 'replication':'BlockdevOptionsReplication', 'sheepdog': 'BlockdevOptionsSheepdog', 'ssh':'BlockdevOptionsSsh', + 'throttle': 'BlockdevOptionsThrottle', 'vdi':'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat', 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.c b/block/throttle.c new file mode 100644 index 00..6484c2ae2a --- /dev/null +++ b/block/throttle.c @@ -0,0 +1,314 @@ +/* + * 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 ThrottleConfi
[Qemu-devel] [PATCH v5 3/6] block: tidy ThrottleGroupMember initializations
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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 9ba800e733..c51fb8c8aa 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -252,9 +252,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(>public.throttle_group_member.throttled_reqs_lock); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>remove_bs_notifiers); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 3b07b25f39..7749cf043f 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(>throttled_reqs_lock); +qemu_co_queue_init(>throttled_reqs[0]); +qemu_co_queue_init(>throttled_reqs[1]); qemu_mutex_unlock(>lock); } -- 2.11.0
[Qemu-devel] [PATCH v5 0/6] add throttle block driver filter
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) v5: fix crash in 'add aio_context field in ThrottleGroupMember' fix suggestions in block/throttle.c v4: fix suggestions in block/throttle.c fix suggestions in block/throttle_groups.c add doc note in BlockDevOptionsThrottle 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 (6): 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 iotest 184 for the throttle filter driver qapi/block-core.json| 70 +++- include/block/throttle-groups.h | 47 ++- include/qemu/throttle-options.h | 60 ++-- include/qemu/throttle.h | 3 + include/sysemu/block-backend.h | 20 +- block/block-backend.c | 62 ++-- block/qapi.c| 8 +- block/throttle-groups.c | 734 +--- block/throttle.c| 314 + blockdev.c | 4 +- tests/test-throttle.c | 111 +++--- util/throttle.c | 151 + block/Makefile.objs | 1 + tests/qemu-iotests/184 | 310 + tests/qemu-iotests/184.out | 422 +++ tests/qemu-iotests/group| 1 + 16 files changed, 1997 insertions(+), 321 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
[Qemu-devel] [PATCH v5 6/6] block: add iotest 184 for the throttle filter driver
Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/184 | 310 + tests/qemu-iotests/184.out | 422 + tests/qemu-iotests/group | 1 + 3 files changed, 733 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 00..5c11d1123d --- /dev/null +++ b/tests/qemu-iotests/184 @@ -0,0 +1,310 @@ +#!/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 qcow2 +_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 <
Re: [Qemu-devel] [Qemu-block] [PATCH v2 1/2] block: use internal filter node in backup
On Wed, Aug 16, 2017 at 02:25:44PM +0100, Stefan Hajnoczi wrote: On Tue, Aug 15, 2017 at 11:18:53AM +0300, Manos Pitsidianakis wrote: block/backup.c currently uses before write notifiers on the targeted node. We can create a filter node instead to intercept write requests for the backup job on the BDS level, instead of the BlockBackend level. This is part of deprecating before write notifiers, which are hard coded into the block layer. Block filter drivers are inserted into the graph only when a feature is needed. This makes the block layer more modular and reuses the block driver abstraction that is already present. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c| 89 +-- block/backup.c | 207 - block/mirror.c | 7 +- blockdev.c | 2 +- include/block/block.h | 8 +- tests/qemu-iotests/141.out | 2 +- 6 files changed, 276 insertions(+), 39 deletions(-) diff --git a/block.c b/block.c index 2de1c29eb3..81bd51b670 100644 --- a/block.c +++ b/block.c @@ -2088,6 +2088,38 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs) } /* + * Sets the file link of a BDS. A new reference is created; callers + * which don't need their own reference any more must call bdrv_unref(). + */ +void bdrv_set_file(BlockDriverState *bs, BlockDriverState *file_bs, + Error **errp) +{ +if (file_bs) { +bdrv_ref(file_bs); +} + +if (bs->file) { +bdrv_unref_child(bs, bs->file); +} + +if (!file_bs) { +bs->file = NULL; +goto out; +} + +bs->file = bdrv_attach_child(bs, file_bs, "file", _file, + errp); +if (!bs->file) { +bdrv_unref(file_bs); +} + +bdrv_refresh_filename(bs); + +out: +bdrv_refresh_limits(bs, NULL); +} + +/* * Sets the backing file link of a BDS. A new reference is created; callers * which don't need their own reference any more must call bdrv_unref(). */ @@ -2355,12 +2387,12 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, goto out; } -/* bdrv_append() consumes a strong reference to bs_snapshot +/* bdrv_append_backing() consumes a strong reference to bs_snapshot * (i.e. it will call bdrv_unref() on it) even on error, so in * order to be able to return one, we have to increase * bs_snapshot's refcount here */ bdrv_ref(bs_snapshot); -bdrv_append(bs_snapshot, bs, _err); +bdrv_append_backing(bs_snapshot, bs, _err); if (local_err) { error_propagate(errp, local_err); bs_snapshot = NULL; @@ -3142,7 +3174,7 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return false; } -if (c->role == _backing) { +if (c->role == _backing || c->role == _file) { /* If @from is a backing file of @to, ignore the child to avoid * creating a loop. We only want to change the pointer of other * parents. */ This may have unwanted side-effects. I think you're using is so that bdrv_set_file() + bdrv_replace_node() does not create a loop in the graph. That is okay if there is only one parent with child_file. If there are multiple parents with that role then it's not clear to me that they should all be skipped. I am afraid I don't understand what you're saying. What is the difference with the child_backing scenario here? In both cases we should update all from->parents children unless they also happen to be a child of `to`. If there are multiple parents with child_file, they are not skipped except for the ones where `to` is the parent. @@ -3213,6 +3245,45 @@ out: } /* + * Add new bs node at the top of a BDS chain while the chain is + * live, while keeping required fields on the top layer. + * + * This will modify the BlockDriverState fields, and swap contents + * between bs_new and bs_top. Both bs_new and bs_top are modified. + * + * bs_new must not be attached to a BlockBackend. + * + * bdrv_append_file() takes ownership of a bs_new reference and unrefs it + * because that's what the callers commonly need. bs_new will be referenced by + * the old parents of bs_top after bdrv_append_file() returns. If the caller + * needs to keep a reference of its own, it must call bdrv_ref(). + */ +void bdrv_append_file(BlockDriverState *bs_new, BlockDriverState *bs_top, + Error **errp) +{ +Error *local_err = NULL; + +bdrv_ref(bs_top); +bdrv_set_file(bs_new, bs_top, _err); bdrv_set_file() takes its own reference so there's no need to call bdrv_ref(bs_top). But it would be more consistent with existing functions for bdrv_set_file() *not* to take a new reference. If you make that change then this bdrv_ref() is correct. +if (local_err) { +error_propagate(errp, local_err);
Re: [Qemu-devel] [PATCH RFC] block: add block-insert-node QMP command
On Wed, Aug 16, 2017 at 06:59:25AM -0500, Eric Blake wrote: On 08/16/2017 04:41 AM, Manos Pitsidianakis wrote: +## +# @block-insert-node: +# +# Insert a filter node between a specific edge in the block driver state graph. +# @parent: the name of the parent node or device +# @node:the name of the node to insert under parent +# @child: the name of the child of both node and parent Is this always going to be between two existing nodes, or can this command also be used to insert at the end of the chain (for example, if parent or child is omitted)? If this is used for filter nodes, I suppose only between would make sense (for now). Is there a use case for the latter? Perhaps. Given a qcow2 image backing chain: base <- active there are four BDS (2 format, 2 protocol). Ideally, I could add filtering to any one of those four nodes (a filter on the base protocol level restricts how much guest data can be used from the backing image, but with no limits on the qcow2 metadata; a filter on the base format level restricts metadata reads as well; similarly for filters on the active protocol and format layers). But adding a filter on 'active' at the format level has no pre-existing parent (I'm adding the filter as the new top-level). Or am I missing something? The parent in this case is the storage device (disk / cdrom), whose name is specified as the parent. The first example in the qapi/block-core.json is such a case. In code I check blk_by_name(parent) and if that doesn't exist, I try with bdrv_find_node(parent). Perhaps I should reword the documentation or did I misunderstand what you wrote? signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH RFC] block: add block-insert-node QMP command
On Tue, Aug 15, 2017 at 05:12:42PM -0500, Eric Blake wrote: On 08/15/2017 02:45 AM, Manos Pitsidianakis wrote: block-insert-node and its pair command block-remove-node provide runtime insertion and removal of filter nodes. block-insert-node takes a (parent, child) and (node, child) pair of edges and unrefs the (parent, child) BdrvChild relationship and creates a new (parent, node) child with the same BdrvChildRole. This is a different approach than x-blockdev-change which uses the driver methods bdrv_add_child() and bdrv_del_child(), Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c| 192 blockdev.c | 44 ++ include/block/block.h |6 + qapi/block-core.json | 60 +++ tests/qemu-iotests/193 | 241 ++ tests/qemu-iotests/193.out | 1116 tests/qemu-iotests/group |1 + 7 files changed, 1660 insertions(+) create mode 100755 tests/qemu-iotests/193 create mode 100644 tests/qemu-iotests/193.out You may want to look at using scripts/git.orderfile, to rearrange your patch so that interface changes (.json, .h) occur before implementation (.c). For now, I'm just focusing on the interface: Thanks for the tip, I will use it from now on! +++ b/qapi/block-core.json @@ -3947,3 +3947,63 @@ 'data' : { 'parent': 'str', '*child': 'str', '*node': 'str' } } + +## +# @block-insert-node: +# +# Insert a filter node between a specific edge in the block driver state graph. +# @parent: the name of the parent node or device +# @node:the name of the node to insert under parent +# @child: the name of the child of both node and parent Is this always going to be between two existing nodes, or can this command also be used to insert at the end of the chain (for example, if parent or child is omitted)? If this is used for filter nodes, I suppose only between would make sense (for now). Is there a use case for the latter? +#} +# <- { 'return': {} } +# +## Missing 'Since: 2.11'. +{ 'command': 'block-insert-node', + 'data': { 'parent': 'str', + 'child': 'str', + 'node': 'str'} } For now, it looks like you require all arguments, and therefore this is always insertion in the middle. +## +# @block-remove-node: +# +# Remove a filter node between two other nodes in the block driver state graph. +# @parent: the name of the parent node or device +# @node:the name of the node to remove from parent +# @child: the name of the child of node which will go under parent +## +{ 'command': 'block-remove-node', + 'data': { 'parent': 'str', + 'child': 'str', + 'node': 'str'} } Likewise missing 2.11. Overall I'm not seeing problems with the interface from the UI perspective, but I have not been paying close attention to your larger efforts on throttling nodes, so I hope other reviewers will chime in. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v2 1/6] block: skip implicit nodes in snapshots, blockjobs
On Tue, Aug 15, 2017 at 03:24:38PM +0200, Alberto Garcia wrote: On Wed 09 Aug 2017 04:02:51 PM CEST, Manos Pitsidianakis wrote: @@ -2988,6 +3004,9 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, return; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_explicit(bs); + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); This change works here because right now the only implicit node that we could have in practice is the throttle node, but I wonder if this is good enough for any kind of implicit node in general. It does feel a bit sloppy but block jobs should work the same with implicit nodes and without, so all we can do is ignore them. +static inline BlockDriverState *child_bs(BlockDriverState *bs) +{ +BdrvChild *child = QLIST_FIRST(>children); +assert(child && !QLIST_NEXT(child, next)); +return child->bs; +} This aborts if the bs has a number of children != 1. That's not something that I would expect from a function named like that. Considering that you're only using it in bdrv_get_first_explicit(), why don't you simply move the code there? It felt useful to have a function that also returns file->bs (we have backing_bs() already) instead of doing backing_bs(bs) ? : file_bs(bs) The other question is of course whether we can rely for the future on the assumption that implicit nodes only have one children. This is only to get either bs->backing or bs->file (we can't have both anyway). I wanted to document it with something like "Used for filter drivers with only one child" which fits with what implicit nodes we have so far (mirror, commit, throttle and in the future backup) signature.asc Description: PGP signature
[Qemu-devel] [PATCH v2 1/2] block: use internal filter node in backup
block/backup.c currently uses before write notifiers on the targeted node. We can create a filter node instead to intercept write requests for the backup job on the BDS level, instead of the BlockBackend level. This is part of deprecating before write notifiers, which are hard coded into the block layer. Block filter drivers are inserted into the graph only when a feature is needed. This makes the block layer more modular and reuses the block driver abstraction that is already present. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c| 89 +-- block/backup.c | 207 - block/mirror.c | 7 +- blockdev.c | 2 +- include/block/block.h | 8 +- tests/qemu-iotests/141.out | 2 +- 6 files changed, 276 insertions(+), 39 deletions(-) diff --git a/block.c b/block.c index 2de1c29eb3..81bd51b670 100644 --- a/block.c +++ b/block.c @@ -2088,6 +2088,38 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs) } /* + * Sets the file link of a BDS. A new reference is created; callers + * which don't need their own reference any more must call bdrv_unref(). + */ +void bdrv_set_file(BlockDriverState *bs, BlockDriverState *file_bs, + Error **errp) +{ +if (file_bs) { +bdrv_ref(file_bs); +} + +if (bs->file) { +bdrv_unref_child(bs, bs->file); +} + +if (!file_bs) { +bs->file = NULL; +goto out; +} + +bs->file = bdrv_attach_child(bs, file_bs, "file", _file, + errp); +if (!bs->file) { +bdrv_unref(file_bs); +} + +bdrv_refresh_filename(bs); + +out: +bdrv_refresh_limits(bs, NULL); +} + +/* * Sets the backing file link of a BDS. A new reference is created; callers * which don't need their own reference any more must call bdrv_unref(). */ @@ -2355,12 +2387,12 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, goto out; } -/* bdrv_append() consumes a strong reference to bs_snapshot +/* bdrv_append_backing() consumes a strong reference to bs_snapshot * (i.e. it will call bdrv_unref() on it) even on error, so in * order to be able to return one, we have to increase * bs_snapshot's refcount here */ bdrv_ref(bs_snapshot); -bdrv_append(bs_snapshot, bs, _err); +bdrv_append_backing(bs_snapshot, bs, _err); if (local_err) { error_propagate(errp, local_err); bs_snapshot = NULL; @@ -3142,7 +3174,7 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return false; } -if (c->role == _backing) { +if (c->role == _backing || c->role == _file) { /* If @from is a backing file of @to, ignore the child to avoid * creating a loop. We only want to change the pointer of other * parents. */ @@ -3213,6 +3245,45 @@ out: } /* + * Add new bs node at the top of a BDS chain while the chain is + * live, while keeping required fields on the top layer. + * + * This will modify the BlockDriverState fields, and swap contents + * between bs_new and bs_top. Both bs_new and bs_top are modified. + * + * bs_new must not be attached to a BlockBackend. + * + * bdrv_append_file() takes ownership of a bs_new reference and unrefs it + * because that's what the callers commonly need. bs_new will be referenced by + * the old parents of bs_top after bdrv_append_file() returns. If the caller + * needs to keep a reference of its own, it must call bdrv_ref(). + */ +void bdrv_append_file(BlockDriverState *bs_new, BlockDriverState *bs_top, + Error **errp) +{ +Error *local_err = NULL; + +bdrv_ref(bs_top); +bdrv_set_file(bs_new, bs_top, _err); +if (local_err) { +error_propagate(errp, local_err); +bdrv_set_file(bs_new, NULL, _abort); +goto out; +} +bdrv_replace_node(bs_top, bs_new, _err); +if (local_err) { +error_propagate(errp, local_err); +goto out; +} + +/* bs_new is now referenced by its new parents, we don't need the + * additional reference any more. */ +out: +bdrv_unref(bs_top); +bdrv_unref(bs_new); +} + +/* * Add new bs contents at the top of an image chain while the chain is * live, while keeping required fields on the top layer. * @@ -3223,13 +3294,13 @@ out: * * This function does not create any image files. * - * bdrv_append() takes ownership of a bs_new reference and unrefs it because - * that's what the callers commonly need. bs_new will be referenced by the old - * parents of bs_top after bdrv_append() returns. If the caller needs to keep a - * reference of its own, it must call bdrv_ref(). + * bdrv_append_backing() takes ownership of a bs_new reference and unrefs it + * because that's what the callers commonly need. bs_new will be reference
[Qemu-devel] [PATCH v2 2/2] block: add filter driver to block/write-threshold.c
With runtime insertion and removal of filters, write-threshold.c can provide more flexible deliveries of BLOCK_WRITE_THRESHOLD events. After the event trigger, the filter nodes are no longer useful and must be removed. The existing write-threshold cannot be easily converted to using the filter driver, so it is not affected. This is part of deprecating before write notifiers, which are hard coded into the block layer. Block filter drivers are inserted into the graph only when a feature is needed. This makes the block layer more modular and reuses the block driver abstraction that is already present. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block/qapi.c| 2 +- block/write-threshold.c | 264 +++- include/block/write-threshold.h | 22 ++-- qapi/block-core.json| 19 ++- tests/test-write-threshold.c| 40 +++--- 5 files changed, 281 insertions(+), 66 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index 2be44a6758..fe6cf2eae5 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -122,7 +122,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->group = g_strdup(throttle_group_get_name(tgm)); } -info->write_threshold = bdrv_write_threshold_get(bs); +info->write_threshold = bdrv_write_threshold_get_legacy(bs); bs0 = bs; p_image_info = >image; diff --git a/block/write-threshold.c b/block/write-threshold.c index 0bd1a01c86..4a67188ea3 100644 --- a/block/write-threshold.c +++ b/block/write-threshold.c @@ -2,9 +2,11 @@ * QEMU System Emulator block write threshold notification * * Copyright Red Hat, Inc. 2014 + * Copyright 2017 Manos Pitsidianakis * * Authors: * Francesco Romani <from...@redhat.com> + * Manos Pitsidianakis <el13...@mail.ntua.gr> * * 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. @@ -19,46 +21,35 @@ #include "qmp-commands.h" -uint64_t bdrv_write_threshold_get(const BlockDriverState *bs) +uint64_t bdrv_write_threshold_get_legacy(const BlockDriverState *bs) { return bs->write_threshold_offset; } -bool bdrv_write_threshold_is_set(const BlockDriverState *bs) +bool bdrv_write_threshold_is_set_legacy(const BlockDriverState *bs) { return bs->write_threshold_offset > 0; } -static void write_threshold_disable(BlockDriverState *bs) +static void write_threshold_disable_legacy(BlockDriverState *bs) { -if (bdrv_write_threshold_is_set(bs)) { +if (bdrv_write_threshold_is_set_legacy(bs)) { notifier_with_return_remove(>write_threshold_notifier); bs->write_threshold_offset = 0; } } -uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, - const BdrvTrackedRequest *req) -{ -if (bdrv_write_threshold_is_set(bs)) { -if (req->offset > bs->write_threshold_offset) { -return (req->offset - bs->write_threshold_offset) + req->bytes; -} -if ((req->offset + req->bytes) > bs->write_threshold_offset) { -return (req->offset + req->bytes) - bs->write_threshold_offset; -} -} -return 0; -} - static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, void *opaque) { BdrvTrackedRequest *req = opaque; BlockDriverState *bs = req->bs; uint64_t amount = 0; +uint64_t threshold = bdrv_write_threshold_get_legacy(bs); +uint64_t offset = req->offset; +uint64_t bytes = req->bytes; -amount = bdrv_write_threshold_exceeded(bs, req); +amount = bdrv_write_threshold_exceeded(threshold, offset, bytes); if (amount > 0) { qapi_event_send_block_write_threshold( bs->node_name, @@ -67,7 +58,7 @@ static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, _abort); /* autodisable to avoid flooding the monitor */ -write_threshold_disable(bs); +write_threshold_disable_legacy(bs); } return 0; /* should always let other notifiers run */ @@ -79,25 +70,26 @@ static void write_threshold_register_notifier(BlockDriverState *bs) bdrv_add_before_write_notifier(bs, >write_threshold_notifier); } -static void write_threshold_update(BlockDriverState *bs, - int64_t threshold_bytes) +static void write_threshold_update_legacy(BlockDriverState *bs, + int64_t threshold_bytes) { bs->write_threshold_offset = threshold_bytes; } -void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes) +void bdrv_write_threshold_set_legacy(BlockDriverState *bs, + uint64_t threshold_bytes) { -if (bdr
[Qemu-devel] [PATCH v2 0/2] add internal backup job and write-threshold filter drivers
This is part of my GSOC project, which is refactoring hardcoded block layer features into filter drivers. Block filter drivers are inserted into the graph only when a feature is needed. This makes the block layer more modular and reuses the block driver abstraction that is already present. Before write notifiers currently have two users: block/backup.c uses before write notifiers to intercept write requests. This can be refactored to use the filter driver interface by injecting an implicit filter node to intercept the write requests and call backup_do_cow(). block/write-threshold.c checks that write requests do not pass a user set offset and issue an event when they do. A new write-threshold driver can perform the same function and be added by the user when block-{insert,remove}-node are introduced. It is not trivial to convert the existing interface (block-set-write-threshold) to using the filter driver. v2: add motivation in commit messages dropped hunk that removed before-write notifiers Manos Pitsidianakis (2): block: use internal filter node in backup block: add filter driver to block/write-threshold.c block.c | 89 -- block/backup.c | 207 +++ block/mirror.c | 7 +- block/qapi.c| 2 +- block/write-threshold.c | 264 +++- blockdev.c | 2 +- include/block/block.h | 8 +- include/block/write-threshold.h | 22 ++-- qapi/block-core.json| 19 ++- tests/qemu-iotests/141.out | 2 +- tests/test-write-threshold.c| 40 +++--- 11 files changed, 557 insertions(+), 105 deletions(-) -- 2.11.0
Re: [Qemu-devel] [PATCH 1/2] block: use internal filter node in backup
On Tue, Aug 15, 2017 at 10:44:15AM +0300, Vladimir Sementsov-Ogievskiy wrote: 15.08.2017 09:19, Manos Pitsidianakis wrote: block/backup.c currently uses before write notifiers on the targeted node. We can create a filter node instead to intercept write requests for the backup job on the BDS level, instead of the BlockBackend level. Hi Manos! Looks interesting but what is the real benefit of it? It doesn't look like it simplifies the code.. Also, is it affect performance? I think it worth describing in commit message. The goal is to refactor the block layer into being more modular instead of having hard coded features. I don't suspect this will have any kind of performance gains, this is a minor change. Before write notifiers are not needed if you can intercept the write requests on the way. signature.asc Description: PGP signature
[Qemu-devel] [PATCH RFC] block: add block-insert-node QMP command
block-insert-node and its pair command block-remove-node provide runtime insertion and removal of filter nodes. block-insert-node takes a (parent, child) and (node, child) pair of edges and unrefs the (parent, child) BdrvChild relationship and creates a new (parent, node) child with the same BdrvChildRole. This is a different approach than x-blockdev-change which uses the driver methods bdrv_add_child() and bdrv_del_child(), Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c| 192 blockdev.c | 44 ++ include/block/block.h |6 + qapi/block-core.json | 60 +++ tests/qemu-iotests/193 | 241 ++ tests/qemu-iotests/193.out | 1116 tests/qemu-iotests/group |1 + 7 files changed, 1660 insertions(+) create mode 100755 tests/qemu-iotests/193 create mode 100644 tests/qemu-iotests/193.out diff --git a/block.c b/block.c index 81bd51b670..f874aabbfb 100644 --- a/block.c +++ b/block.c @@ -930,6 +930,9 @@ static void bdrv_backing_attach(BdrvChild *c) parent->backing_blocker); bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM, parent->backing_blocker); +/* Unblock filter node insertion */ +bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_EDGE_MODIFICATION, +parent->backing_blocker); /* * We do backup in 3 ways: * 1. drive backup @@ -5036,3 +5039,192 @@ BlockDriverState *bdrv_get_first_explicit(BlockDriverState *bs) } return bs; } + + +static inline BdrvChild *bdrv_find_child(BlockDriverState *parent_bs, + const char *child_name) +{ +BdrvChild *child; +assert(child_name); + +QLIST_FOREACH(child, _bs->children, next) { +if (child->bs && !g_strcmp0(child->bs->node_name, child_name)) { +return child; +} +} + +return NULL; +} + +static int check_node_edge(const char *parent, const char *child, Error **errp) +{ +BlockDriverState *parent_bs, *child_bs; +parent_bs = bdrv_find_node(parent); +if (!parent_bs) { +error_setg(errp, "'%s' not a node name", parent); +return 1; +} +child_bs = bdrv_find_node(child); +if (!child_bs) { +error_setg(errp, "'%s' not a node name", child); +return 1; +} +if (!bdrv_find_child(parent_bs, child)) { +error_setg(errp, "'%s' not a child of '%s'", child, parent); +return 1; +} +if (bdrv_op_is_blocked(parent_bs, BLOCK_OP_TYPE_EDGE_MODIFICATION, errp) || +bdrv_op_is_blocked(child_bs, BLOCK_OP_TYPE_EDGE_MODIFICATION, errp)) { +return 1; +} +return 0; +} + +void bdrv_insert_node(const char *parent, const char *child, + const char *node, Error **errp) +{ +BlockBackend *blk; +BlockDriverState *parent_bs, *node_bs, *child_bs; +BdrvChild *c; +const BdrvChildRole *role; + +if (check_node_edge(node, child, errp)) { +return; +} +node_bs = bdrv_find_node(node); +child_bs = bdrv_find_node(child); +blk = blk_by_name(parent); +if (blk) { +/* insert 'node' as root bs of 'parent' device */ +if (!blk_bs(blk)) { +error_setg(errp, "Device '%s' has no medium", parent); +return; +} +if (blk_bs(blk) != child_bs) { +error_setg(errp, "'%s' not a child of device '%s'", child, parent); +return; +} +bdrv_drained_begin(child_bs); +blk_remove_bs(blk); +blk_insert_bs(blk, node_bs, errp); +if (!blk_bs(blk)) { +blk_insert_bs(blk, child_bs, _abort); +} +bdrv_drained_end(child_bs); +return; +} + +/* insert 'node' as child bs of 'parent' node */ +if (check_node_edge(parent, child, errp)) { +return; +} +parent_bs = bdrv_find_node(parent); +c = bdrv_find_child(parent_bs, child); +role = c->role; +assert(role == _file || role == _backing); + +bdrv_ref(node_bs); + +bdrv_drained_begin(parent_bs); +bdrv_unref_child(parent_bs, c); +if (role == _file) { +parent_bs->file = bdrv_attach_child(parent_bs, node_bs, "file", +_file, errp); +if (!parent_bs->file) { +parent_bs->file = bdrv_attach_child(parent_bs, child_bs, "file", +_file, _abort); +goto out; +} +} else if (role == _backing) { +parent_bs->backing = bdrv_attach_child(parent_bs, node_bs, "backing", + _backing, errp); +if (!parent_bs->backing) { +parent_bs->backing = bdrv_attach_child(parent_bs, child_bs, +
[Qemu-devel] [PATCH 2/2] block: add filter driver to block/write-threshold.c
With runtime insertion and removal of filters, write-threshold.c can provide more flexible deliveries of BLOCK_WRITE_THRESHOLD events. After the event trigger, the filter nodes are no longer useful and must be removed. The existing write-threshold cannot be easily converted to using the filter driver, so it is not affected. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block/qapi.c| 2 +- block/write-threshold.c | 264 +++- include/block/write-threshold.h | 22 ++-- qapi/block-core.json| 19 ++- tests/test-write-threshold.c| 40 +++--- 5 files changed, 281 insertions(+), 66 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index 2be44a6758..fe6cf2eae5 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -122,7 +122,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->group = g_strdup(throttle_group_get_name(tgm)); } -info->write_threshold = bdrv_write_threshold_get(bs); +info->write_threshold = bdrv_write_threshold_get_legacy(bs); bs0 = bs; p_image_info = >image; diff --git a/block/write-threshold.c b/block/write-threshold.c index 0bd1a01c86..4a67188ea3 100644 --- a/block/write-threshold.c +++ b/block/write-threshold.c @@ -2,9 +2,11 @@ * QEMU System Emulator block write threshold notification * * Copyright Red Hat, Inc. 2014 + * Copyright 2017 Manos Pitsidianakis * * Authors: * Francesco Romani <from...@redhat.com> + * Manos Pitsidianakis <el13...@mail.ntua.gr> * * 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. @@ -19,46 +21,35 @@ #include "qmp-commands.h" -uint64_t bdrv_write_threshold_get(const BlockDriverState *bs) +uint64_t bdrv_write_threshold_get_legacy(const BlockDriverState *bs) { return bs->write_threshold_offset; } -bool bdrv_write_threshold_is_set(const BlockDriverState *bs) +bool bdrv_write_threshold_is_set_legacy(const BlockDriverState *bs) { return bs->write_threshold_offset > 0; } -static void write_threshold_disable(BlockDriverState *bs) +static void write_threshold_disable_legacy(BlockDriverState *bs) { -if (bdrv_write_threshold_is_set(bs)) { +if (bdrv_write_threshold_is_set_legacy(bs)) { notifier_with_return_remove(>write_threshold_notifier); bs->write_threshold_offset = 0; } } -uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, - const BdrvTrackedRequest *req) -{ -if (bdrv_write_threshold_is_set(bs)) { -if (req->offset > bs->write_threshold_offset) { -return (req->offset - bs->write_threshold_offset) + req->bytes; -} -if ((req->offset + req->bytes) > bs->write_threshold_offset) { -return (req->offset + req->bytes) - bs->write_threshold_offset; -} -} -return 0; -} - static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, void *opaque) { BdrvTrackedRequest *req = opaque; BlockDriverState *bs = req->bs; uint64_t amount = 0; +uint64_t threshold = bdrv_write_threshold_get_legacy(bs); +uint64_t offset = req->offset; +uint64_t bytes = req->bytes; -amount = bdrv_write_threshold_exceeded(bs, req); +amount = bdrv_write_threshold_exceeded(threshold, offset, bytes); if (amount > 0) { qapi_event_send_block_write_threshold( bs->node_name, @@ -67,7 +58,7 @@ static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, _abort); /* autodisable to avoid flooding the monitor */ -write_threshold_disable(bs); +write_threshold_disable_legacy(bs); } return 0; /* should always let other notifiers run */ @@ -79,25 +70,26 @@ static void write_threshold_register_notifier(BlockDriverState *bs) bdrv_add_before_write_notifier(bs, >write_threshold_notifier); } -static void write_threshold_update(BlockDriverState *bs, - int64_t threshold_bytes) +static void write_threshold_update_legacy(BlockDriverState *bs, + int64_t threshold_bytes) { bs->write_threshold_offset = threshold_bytes; } -void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes) +void bdrv_write_threshold_set_legacy(BlockDriverState *bs, + uint64_t threshold_bytes) { -if (bdrv_write_threshold_is_set(bs)) { +if (bdrv_write_threshold_is_set_legacy(bs)) { if (threshold_bytes > 0) { -write_threshold_update(bs, threshold_bytes); +write_threshold_update_legacy(bs, threshold_bytes); } else { -write_threshold_d
[Qemu-devel] [PATCH 1/2] block: use internal filter node in backup
block/backup.c currently uses before write notifiers on the targeted node. We can create a filter node instead to intercept write requests for the backup job on the BDS level, instead of the BlockBackend level. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c| 89 +-- block/backup.c | 207 - block/io.c | 10 +-- block/mirror.c | 4 +- blockdev.c | 2 +- include/block/block.h | 8 +- tests/qemu-iotests/141.out | 2 +- 7 files changed, 277 insertions(+), 45 deletions(-) diff --git a/block.c b/block.c index 2de1c29eb3..81bd51b670 100644 --- a/block.c +++ b/block.c @@ -2088,6 +2088,38 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs) } /* + * Sets the file link of a BDS. A new reference is created; callers + * which don't need their own reference any more must call bdrv_unref(). + */ +void bdrv_set_file(BlockDriverState *bs, BlockDriverState *file_bs, + Error **errp) +{ +if (file_bs) { +bdrv_ref(file_bs); +} + +if (bs->file) { +bdrv_unref_child(bs, bs->file); +} + +if (!file_bs) { +bs->file = NULL; +goto out; +} + +bs->file = bdrv_attach_child(bs, file_bs, "file", _file, + errp); +if (!bs->file) { +bdrv_unref(file_bs); +} + +bdrv_refresh_filename(bs); + +out: +bdrv_refresh_limits(bs, NULL); +} + +/* * Sets the backing file link of a BDS. A new reference is created; callers * which don't need their own reference any more must call bdrv_unref(). */ @@ -2355,12 +2387,12 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, goto out; } -/* bdrv_append() consumes a strong reference to bs_snapshot +/* bdrv_append_backing() consumes a strong reference to bs_snapshot * (i.e. it will call bdrv_unref() on it) even on error, so in * order to be able to return one, we have to increase * bs_snapshot's refcount here */ bdrv_ref(bs_snapshot); -bdrv_append(bs_snapshot, bs, _err); +bdrv_append_backing(bs_snapshot, bs, _err); if (local_err) { error_propagate(errp, local_err); bs_snapshot = NULL; @@ -3142,7 +3174,7 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return false; } -if (c->role == _backing) { +if (c->role == _backing || c->role == _file) { /* If @from is a backing file of @to, ignore the child to avoid * creating a loop. We only want to change the pointer of other * parents. */ @@ -3213,6 +3245,45 @@ out: } /* + * Add new bs node at the top of a BDS chain while the chain is + * live, while keeping required fields on the top layer. + * + * This will modify the BlockDriverState fields, and swap contents + * between bs_new and bs_top. Both bs_new and bs_top are modified. + * + * bs_new must not be attached to a BlockBackend. + * + * bdrv_append_file() takes ownership of a bs_new reference and unrefs it + * because that's what the callers commonly need. bs_new will be referenced by + * the old parents of bs_top after bdrv_append_file() returns. If the caller + * needs to keep a reference of its own, it must call bdrv_ref(). + */ +void bdrv_append_file(BlockDriverState *bs_new, BlockDriverState *bs_top, + Error **errp) +{ +Error *local_err = NULL; + +bdrv_ref(bs_top); +bdrv_set_file(bs_new, bs_top, _err); +if (local_err) { +error_propagate(errp, local_err); +bdrv_set_file(bs_new, NULL, _abort); +goto out; +} +bdrv_replace_node(bs_top, bs_new, _err); +if (local_err) { +error_propagate(errp, local_err); +goto out; +} + +/* bs_new is now referenced by its new parents, we don't need the + * additional reference any more. */ +out: +bdrv_unref(bs_top); +bdrv_unref(bs_new); +} + +/* * Add new bs contents at the top of an image chain while the chain is * live, while keeping required fields on the top layer. * @@ -3223,13 +3294,13 @@ out: * * This function does not create any image files. * - * bdrv_append() takes ownership of a bs_new reference and unrefs it because - * that's what the callers commonly need. bs_new will be referenced by the old - * parents of bs_top after bdrv_append() returns. If the caller needs to keep a - * reference of its own, it must call bdrv_ref(). + * bdrv_append_backing() takes ownership of a bs_new reference and unrefs it + * because that's what the callers commonly need. bs_new will be referenced by + * the old parents of bs_top after bdrv_append_backing() returns. If the caller + * needs to keep a reference of its own, it must call bdrv_ref(). */ -void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, -
[Qemu-devel] [PATCH 0/2] add internal backup job and write-threshold filter drivers
Before write notifiers currently have two users: block/backup.c uses before write notifiers to intercept write requests. This can be refactored to use the filter driver interface by injecting an implicit filter node to intercept the write requests and call backup_do_cow(). block/write-threshold.c checks that write requests do not pass a user set offset and issue an event when they do. A new write-threshold driver can perform the same function and be added by the user when block-{insert,remove}-node are introduced. It is not trivial to convert the existing interface (block-set-write-threshold) to using the filter driver. Based-on: <20170809140256.25584-1-el13...@mail.ntua.gr> Manos Pitsidianakis (2): block: use internal filter node in backup block: add filter driver to block/write-threshold.c block.c | 89 -- block/backup.c | 207 +++ block/io.c | 10 +- block/mirror.c | 4 +- block/qapi.c| 2 +- block/write-threshold.c | 264 +++- blockdev.c | 2 +- include/block/block.h | 8 +- include/block/write-threshold.h | 22 ++-- qapi/block-core.json| 19 ++- tests/qemu-iotests/141.out | 2 +- tests/test-write-threshold.c| 40 +++--- 12 files changed, 558 insertions(+), 111 deletions(-) -- 2.11.0
Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
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 access
Re: [Qemu-devel] [PATCH v4 5/7] block: add throttle block filter driver
On Wed, Aug 09, 2017 at 01:07:32PM +0300, Manos Pitsidianakis wrote: +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); ^ Tried some write throttling testing, noticed this. If anyone wants to test this iteration, change this to bdrv_co_pwritev(), I will correct this in the next version. (let's pretend this never happened!) signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v4 5/7] block: add throttle block filter driver
On Thu, Aug 10, 2017 at 03:54:02PM +0200, Alberto Garcia wrote: On Wed 09 Aug 2017 12:07:32 PM CEST, Manos Pitsidianakis wrote: +/* Extract ThrottleConfig options. Assumes cfg is initialized and will be + * checked for validity. + * + * Returns -1 and sets errp if a burst_length value is over UINT_MAX. + */ +static int throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg, +Error **errp) +{ +#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); [...] This is all the code that I was saying that we'd save if we don't allow setting limits here. +static int throttle_configure_tgm(BlockDriverState *bs, + ThrottleGroupMember *tgm, + QDict *options, Error **errp) +{ +int ret; +ThrottleConfig cfg; +const char *group_name = NULL; No need to set it to NULL here. I know, I do it out of habit! +Error *local_err = NULL; +QemuOpts *opts = qemu_opts_create(_opts, NULL, 0, _abort); + +qemu_opts_absorb_qdict(opts, options, _err); +if (local_err) { +error_propagate(errp, local_err); +goto err; +} + +/* If group_name is NULL, 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, ); + +/* Change limits if user has specified them */ +if (throttle_extract_options(opts, , errp) || +!throttle_is_valid(, errp)) { +throttle_group_unregister_tgm(tgm); +goto err; +} +/* Update group configuration */ +throttle_group_config(tgm, ); We'd also spare this, and this function would remain much simpler. + +ret = 0; +goto fin; + +err: +ret = -EINVAL; +fin: +qemu_opts_del(opts); +return ret; +} If you set ret = -EINVAL before calling goto err you can simplify this part as well, but feel free to ignore this suggestion. +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); +} I would rename 'reopen_state' as 'state' for consistency with the other two functions. The function signatures in block_int.h have reopen_state, so maybe for consistency I should change the other two to reopen_state as well, instead. +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; +} I also find the mixing of state->bs->opaque and tgm a bit confusing. Here's a suggestion, but feel free to ignore it: You're right, though it's only a few lines it might require a second read. I will rewrite those more clearly, too. signature.asc Description: PGP signature
[Qemu-devel] [PATCH v2 4/6] block: remove legacy I/O throttling
This commit removes all I/O throttling from block/block-backend.c. In order to support the existing interface, it is changed to use the block/throttle.c filter driver. The throttle filter node that is created by the legacy interface is stored in a 'throttle_node' field in the BlockBackendPublic of the device. The legacy throttle node is managed by the legacy interface completely. More advanced configurations with the filter drive are possible using the QMP API, but these will be ignored by the legacy interface. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block/block-backend.c | 133 block/qapi.c| 10 +-- block/throttle.c| 8 +++ blockdev.c | 49 +++ include/block/throttle-groups.h | 1 + include/sysemu/block-backend.h | 6 +- tests/test-throttle.c | 19 +++--- 7 files changed, 146 insertions(+), 80 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index df0200fc49..61983b7393 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -15,6 +15,7 @@ #include "block/block_int.h" #include "block/blockjob.h" #include "block/throttle-groups.h" +#include "qemu/throttle-options.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "qapi-event.h" @@ -282,7 +283,7 @@ static void blk_delete(BlockBackend *blk) assert(!blk->refcnt); assert(!blk->name); assert(!blk->dev); -if (blk->public.throttle_group_member.throttle_state) { +if (blk->public.throttle_node) { blk_io_limits_disable(blk); } if (blk->root) { @@ -593,13 +594,7 @@ BlockBackend *blk_by_public(BlockBackendPublic *public) */ void blk_remove_bs(BlockBackend *blk) { -ThrottleTimers *tt; - notifier_list_notify(>remove_bs_notifiers, blk); -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_detach_aio_context(tt); -} blk_update_root_state(blk); @@ -620,12 +615,6 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) bdrv_ref(bs); notifier_list_notify(>insert_bs_notifiers, blk); -if (blk->public.throttle_group_member.throttle_state) { -throttle_timers_attach_aio_context( ->public.throttle_group_member.throttle_timers, -bdrv_get_aio_context(bs)); -} - return 0; } @@ -983,13 +972,6 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, } bdrv_inc_in_flight(bs); - -/* throttling disk I/O */ -if (blk->public.throttle_group_member.throttle_state) { - throttle_group_co_io_limits_intercept(>public.throttle_group_member, -bytes, false); -} - ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); bdrv_dec_in_flight(bs); return ret; @@ -1010,11 +992,6 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, } bdrv_inc_in_flight(bs); -/* throttling disk I/O */ -if (blk->public.throttle_group_member.throttle_state) { - throttle_group_co_io_limits_intercept(>public.throttle_group_member, -bytes, true); -} if (!blk->enable_write_cache) { flags |= BDRV_REQ_FUA; @@ -1682,16 +1659,9 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) { BlockDriverState *bs = blk_bs(blk); -ThrottleGroupMember *tgm = >public.throttle_group_member; if (bs) { -if (tgm->throttle_state) { -throttle_group_detach_aio_context(tgm); -} bdrv_set_aio_context(bs, new_context); -if (tgm->throttle_state) { -throttle_group_attach_aio_context(tgm, new_context); -} } } @@ -1909,45 +1879,98 @@ int blk_commit_all(void) /* throttling disk I/O limits */ void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) { -throttle_group_config(>public.throttle_group_member, cfg); +assert(blk->public.throttle_node); +throttle_group_config(throttle_get_tgm(blk->public.throttle_node), cfg); } void blk_io_limits_disable(BlockBackend *blk) { -assert(blk->public.throttle_group_member.throttle_state); -bdrv_drained_begin(blk_bs(blk)); -throttle_group_unregister_tgm(>public.throttle_group_member); -bdrv_drained_end(blk_bs(blk)); +BlockDriverState *bs, *throttle_node; + +throttle_node = blk_get_public(blk)->throttle_node; + +assert(throttle_node); + +bs = throttle_node->file->bs; +bdrv_drained_begin(bs); + +/* Ref throttle_node's child bs to ensure it won't go away */ +bdrv_ref(bs); + +bdrv_child_try_set_perm(throttle_node->
[Qemu-devel] [PATCH v2 0/6] block: remove legacy I/O throttling
This series depends on my other series 'add throttle block driver filter' currently on v4. Replacing the current I/O interface means the user will use the same options as before and QEMU will create a hidden throttle filter node beneath the device's BlockBackend. v2: new commit: require job-id when device is a node name new commit: remove BlockBackendPublic new commit: add dedicated iotest cleanup reference counting in block/block-backend.c functions add new function to get filter child bs take ownership of options in bdrv_new_open_driver() Manos Pitsidianakis (6): block: skip implicit nodes in snapshots, blockjobs block: add options parameter to bdrv_new_open_driver() block: require job-id when device is a node name block: remove legacy I/O throttling block: add iotest 191 for legacy throttling interface block: remove BlockBackendPublic block.c | 26 ++- block/block-backend.c | 152 +++- block/commit.c | 4 +- block/mirror.c | 2 +- block/qapi.c| 24 +++ block/throttle.c| 8 +++ block/vvfat.c | 2 +- blockdev.c | 148 +- blockjob.c | 16 ++--- include/block/block.h | 2 +- include/block/block_int.h | 9 +++ include/block/blockjob_int.h| 3 +- include/block/throttle-groups.h | 1 + include/sysemu/block-backend.h | 16 + tests/qemu-iotests/191 | 138 tests/qemu-iotests/191.out | 5 ++ tests/qemu-iotests/group| 1 + tests/test-blockjob.c | 10 +-- tests/test-throttle.c | 19 ++--- 19 files changed, 440 insertions(+), 146 deletions(-) create mode 100644 tests/qemu-iotests/191 create mode 100644 tests/qemu-iotests/191.out -- 2.11.0
[Qemu-devel] [PATCH v2 3/6] block: require job-id when device is a node name
With implicit filter nodes on the top of the graph it is not possible to generate job-ids with the name of the device in block_job_create() anymore, since the job's bs will not be a child_root. Instead we can require that job-id is not NULL in block_job_create(), and check that a job-id has been set in the callers of block_job_create() in blockdev.c. It is more consistent to require an explicit job-id when the device parameter in the job creation command, eg { "execute": "drive-backup", "arguments": { "device": "drive0", "sync": "full", "target": "backup.img" } } is not a BlockBackend name, instead of automatically getting it from the root BS if device is a node name. That information is lost after calling block_job_create(), so we can do it in its caller instead. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- blockdev.c | 65 +++- blockjob.c | 16 --- include/block/blockjob_int.h | 3 +- tests/test-blockjob.c| 10 ++- 4 files changed, 67 insertions(+), 27 deletions(-) diff --git a/blockdev.c b/blockdev.c index fc7b65c3f0..6ffa5b0b04 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3004,6 +3004,16 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, return; } +/* Always require a job-id when device is a node name */ +if (!has_job_id) { +if (blk_by_name(device)) { +job_id = device; +} else { +error_setg(errp, "An explicit job ID is required for this node"); +return; +} +} + /* Skip implicit filter nodes */ bs = bdrv_get_first_explicit(bs); @@ -3058,7 +3068,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, /* backing_file string overrides base bs filename */ base_name = has_backing_file ? backing_file : base_name; -stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name, +stream_start(job_id, bs, base_bs, base_name, has_speed ? speed : 0, on_error, _err); if (local_err) { error_propagate(errp, local_err); @@ -3117,6 +3127,16 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, /* Skip implicit filter nodes */ bs = bdrv_get_first_explicit(bs); +/* Always require a job-id when device is a node name */ +if (!has_job_id) { +if (blk_by_name(device)) { +job_id = device; +} else { +error_setg(errp, "An explicit job ID is required for this node"); +return; +} +} + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3171,7 +3191,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, " but 'top' is the active layer"); goto out; } -commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, +commit_active_start(job_id, bs, base_bs, BLOCK_JOB_DEFAULT, speed, on_error, filter_node_name, NULL, NULL, false, _err); } else { @@ -3179,7 +3199,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { goto out; } -commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed, +commit_start(job_id, bs, base_bs, top_bs, speed, on_error, has_backing_file ? backing_file : NULL, filter_node_name, _err); } @@ -3220,7 +3240,13 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; } if (!backup->has_job_id) { -backup->job_id = NULL; +/* Always require a job-id when device is a node name */ +if (blk_by_name(backup->device)) { +backup->job_id = backup->device; +} else { +error_setg(errp, "An explicit job ID is required for this node"); +return NULL; +} } if (!backup->has_compress) { backup->compress = false; @@ -3366,7 +3392,13 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT; } if (!backup->has_job_id) { -backup->job_id = NULL; +/* Always require a job-id when device is a node name */ +if (blk_by_name(backup->device)) { +backup->job_id = backup->device; +} else { +error_setg(errp, "An explicit job ID is required for this node");
[Qemu-devel] [PATCH v2 5/6] block: add iotest 191 for legacy throttling interface
Check that the implicit throttle filter driver node, used for compatibility with the legacy throttling interface on the BlockBackend level, works. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/191 | 138 + tests/qemu-iotests/191.out | 5 ++ tests/qemu-iotests/group | 1 + 3 files changed, 144 insertions(+) create mode 100644 tests/qemu-iotests/191 create mode 100644 tests/qemu-iotests/191.out diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 new file mode 100644 index 00..82fd0b2fe5 --- /dev/null +++ b/tests/qemu-iotests/191 @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Tests that the legacy throttling interface using an implicit throttle filter +# driver node works +# +# 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/>. +# + +import os +import iotests + +class TestLegacyThrottling(iotests.QMPTestCase): +test_img = os.path.join(iotests.test_dir, "test.img") +target_img = os.path.join(iotests.test_dir, "target.img") +base_img = os.path.join(iotests.test_dir, "base.img") + +def setUp(self): +iotests.qemu_img("create", "-f", iotests.imgfmt, self.base_img, "1G") +iotests.qemu_img("create", "-f", iotests.imgfmt, self.test_img, "-b", self.base_img) +iotests.qemu_io("-f", iotests.imgfmt, "-c", "write -P0x5d 1M 128M", self.test_img) +self.vm = iotests.VM().add_drive(self.test_img) +self.vm.launch() + +def tearDown(self): +self.do_check_throttle_node(expect=True) +params = {"device": "drive0", + "bps": 0, + "bps_rd": 0, + "bps_wr": 0, + "iops": 0, + "iops_rd": 0, + "iops_wr": 0, + } +""" +This must remove the implicit throttle_node +""" +result = self.vm.qmp("block_set_io_throttle", conv_keys=False, + **params) +self.do_check_throttle_node(expect=False) +self.vm.shutdown() + +def do_test_job(self, cmd, **args): +params = {"device": "drive0", + "bps": 1024, + "bps_rd": 0, + "bps_wr": 0, + "iops": 0, + "iops_rd": 0, + "iops_wr": 0, + } +result = self.vm.qmp("block_set_io_throttle", conv_keys=False, + **params) +self.assert_qmp(result, "return", {}) +result = self.vm.qmp(cmd, **args) +self.assert_qmp(result, "return", {}) +result = self.vm.qmp("query-block-jobs") +self.assert_qmp(result, "return[0]/device", "drive0") + +def do_check_throttle_node(self, expect): +result = self.vm.qmp("query-named-block-nodes") +for r in result["return"]: +if r["drv"] == "throttle": +self.assertTrue(expect) +return +if expect: +""" throttle_node missing! """ +self.assertTrue(False) + +def do_check_params(self, file): +result = self.vm.qmp("query-block") +self.assert_qmp(result, "return[0]/inserted/bps", 1024) +self.assert_qmp(result, "return[0]/inserted/drv", iotests.imgfmt) +self.assert_qmp(result, "return[0]/inserted/file", file) + +""" +Check that query-block reports the correct throttling parameters while +ignoring the implicit throttle node. +""" +def test_query_block(self): +params = {"device": "drive0", + "bps": 1024, + "bps_rd": 0, + "bps_wr": 0, + "iops": 0, + "iops_rd": 0
[Qemu-devel] [PATCH v2 1/6] block: skip implicit nodes in snapshots, blockjobs
Implicit filter nodes added at the top of nodes can interfere with block jobs. This is not a problem when they are added by other jobs since adding another job will issue a QERR_DEVICE_IN_USE, but it can happen in the next commit which introduces an implicitly created throttle filter node below BlockBackend, which we want to be skipped during automatic operations on the graph since the user does not necessarily know about their existence. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c | 10 ++ block/qapi.c | 14 +- blockdev.c| 34 ++ include/block/block_int.h | 9 + 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/block.c b/block.c index 3615a6809e..e35d546c08 100644 --- a/block.c +++ b/block.c @@ -4945,3 +4945,13 @@ bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, return drv->bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp); } + +/* Get first explicit node down a bs chain. */ +BlockDriverState *bdrv_get_first_explicit(BlockDriverState *bs) +{ +while (bs && bs->drv && bs->implicit) { +bs = child_bs(bs); +assert(bs); +} +return bs; +} diff --git a/block/qapi.c b/block/qapi.c index 7fa2437923..847b044d13 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -147,9 +147,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, /* Skip automatically inserted nodes that the user isn't aware of for * query-block (blk != NULL), but not for query-named-block-nodes */ -while (blk && bs0->drv && bs0->implicit) { -bs0 = backing_bs(bs0); -assert(bs0); +if (blk) { +bs0 = bdrv_get_first_explicit(bs0); } } @@ -336,9 +335,7 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, char *qdev; /* Skip automatically inserted nodes that the user isn't aware of */ -while (bs && bs->drv && bs->implicit) { -bs = backing_bs(bs); -} +bs = bdrv_get_first_explicit(bs); info->device = g_strdup(blk_name(blk)); info->type = g_strdup("unknown"); @@ -465,9 +462,8 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, /* Skip automatically inserted nodes that the user isn't aware of in * a BlockBackend-level command. Stay at the exact node for a node-level * command. */ -while (blk_level && bs->drv && bs->implicit) { -bs = backing_bs(bs); -assert(bs); +if (blk_level) { +bs = bdrv_get_first_explicit(bs); } if (bdrv_get_node_name(bs)[0]) { diff --git a/blockdev.c b/blockdev.c index 23475abb72..fc7b65c3f0 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1300,6 +1300,10 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, if (!bs) { return NULL; } + +/* Skip implicit filter nodes */ +bs = bdrv_get_first_explicit(bs); + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -1508,6 +1512,9 @@ static void internal_snapshot_prepare(BlkActionState *common, return; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_explicit(bs); + /* AioContext is released in .clean() */ state->aio_context = bdrv_get_aio_context(bs); aio_context_acquire(state->aio_context); @@ -1664,6 +1671,9 @@ static void external_snapshot_prepare(BlkActionState *common, return; } +/* Skip implicit filter nodes */ +state->old_bs = bdrv_get_first_explicit(state->old_bs); + /* Acquire AioContext now so any threads operating on old_bs stop */ state->aio_context = bdrv_get_aio_context(state->old_bs); aio_context_acquire(state->aio_context); @@ -1844,6 +1854,9 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) return; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_explicit(bs); + /* AioContext is released in .clean() */ state->aio_context = bdrv_get_aio_context(bs); aio_context_acquire(state->aio_context); @@ -1908,6 +1921,9 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) return; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_explicit(bs); + target = bdrv_lookup_bs(backup->target, backup->target, errp); if (!target) { return; @@ -2988,6 +3004,9 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, return; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_explicit(bs); + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3095,6 +3114,9 @@ void qmp_block_commit(bool has_job
[Qemu-devel] [PATCH v2 2/6] block: add options parameter to bdrv_new_open_driver()
Allow passing a QDict *options parameter to bdrv_new_open_driver() so that it can be used if a driver needs it upon creation. The previous behaviour (empty bs->options and bs->explicit_options) remains when options is NULL. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c | 16 +--- block/commit.c| 4 ++-- block/mirror.c| 2 +- block/vvfat.c | 2 +- include/block/block.h | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/block.c b/block.c index e35d546c08..2de1c29eb3 100644 --- a/block.c +++ b/block.c @@ -1153,16 +1153,26 @@ open_failed: return ret; } +/* + * If options is not NULL, its ownership is transferred to the block layer. The + * caller must use QINCREF() if they wish to keep ownership. + */ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp) + int flags, QDict *options, Error **errp) { BlockDriverState *bs; int ret; bs = bdrv_new(); bs->open_flags = flags; -bs->explicit_options = qdict_new(); -bs->options = qdict_new(); +if (options) { +bs->explicit_options = qdict_clone_shallow(options); +bs->options = qdict_clone_shallow(options); +QDECREF(options); +} else { +bs->explicit_options = qdict_new(); +bs->options = qdict_new(); +} bs->opaque = NULL; update_options_from_flags(bs->options, flags); diff --git a/block/commit.c b/block/commit.c index c7857c3321..539e23c3f8 100644 --- a/block/commit.c +++ b/block/commit.c @@ -342,7 +342,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, /* Insert commit_top block node above top, so we can block consistent read * on the backing chain below it */ commit_top_bs = bdrv_new_open_driver(_commit_top, filter_node_name, 0, - errp); + NULL, errp); if (commit_top_bs == NULL) { goto fail; } @@ -494,7 +494,7 @@ int bdrv_commit(BlockDriverState *bs) backing_file_bs = backing_bs(bs); commit_top_bs = bdrv_new_open_driver(_commit_top, NULL, BDRV_O_RDWR, - _err); + NULL, _err); if (commit_top_bs == NULL) { error_report_err(local_err); goto ro_cleanup; diff --git a/block/mirror.c b/block/mirror.c index c9a6a3ca86..e1a160e6ea 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1164,7 +1164,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, * reads on the top, while disabling it in the intermediate nodes, and make * the backing chain writable. */ mirror_top_bs = bdrv_new_open_driver(_mirror_top, filter_node_name, - BDRV_O_RDWR, errp); + BDRV_O_RDWR, NULL, errp); if (mirror_top_bs == NULL) { return; } diff --git a/block/vvfat.c b/block/vvfat.c index a9e207f7f0..6c59473baf 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3194,7 +3194,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) #endif backing = bdrv_new_open_driver(_write_target, NULL, BDRV_O_ALLOW_RDWR, - _abort); + NULL, _abort); *(void**) backing->opaque = s; bdrv_set_backing_hd(s->bs, backing, _abort); diff --git a/include/block/block.h b/include/block/block.h index ab80195378..d1f03cb48b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -263,7 +263,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp); BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp); + int flags, QDict *options, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, int flags); -- 2.11.0
[Qemu-devel] [PATCH v2 6/6] block: remove BlockBackendPublic
All BlockBackend level throttling (via the implicit throttle filter node) is done in block/block-backend.c and block/throttle-groups.c doesn't know about BlockBackends anymore. Since BlockBackendPublic is not needed anymore, remove it. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block/block-backend.c | 43 +++--- block/qapi.c | 4 ++-- blockdev.c | 6 +++--- include/sysemu/block-backend.h | 12 +--- 4 files changed, 25 insertions(+), 40 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 61983b7393..05f6e67222 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -36,7 +36,10 @@ struct BlockBackend { DriveInfo *legacy_dinfo;/* null unless created by drive_new() */ QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ -BlockBackendPublic public; + +/* implicit throttle filter node for backwards compatibility with legacy + * throttling commands */ +BlockDriverState *throttle_node; void *dev; /* attached device model, if any */ bool legacy_dev;/* true if dev is not a DeviceState */ @@ -283,7 +286,7 @@ static void blk_delete(BlockBackend *blk) assert(!blk->refcnt); assert(!blk->name); assert(!blk->dev); -if (blk->public.throttle_node) { +if (blk->throttle_node) { blk_io_limits_disable(blk); } if (blk->root) { @@ -574,19 +577,11 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) } /* - * Returns a pointer to the publicly accessible fields of @blk. + * Returns the throttle_node field of @blk. */ -BlockBackendPublic *blk_get_public(BlockBackend *blk) +BlockDriverState *blk_get_throttle_node(BlockBackend *blk) { -return >public; -} - -/* - * Returns a BlockBackend given the associated @public fields. - */ -BlockBackend *blk_by_public(BlockBackendPublic *public) -{ -return container_of(public, BlockBackend, public); +return blk->throttle_node; } /* @@ -1879,15 +1874,15 @@ int blk_commit_all(void) /* throttling disk I/O limits */ void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) { -assert(blk->public.throttle_node); -throttle_group_config(throttle_get_tgm(blk->public.throttle_node), cfg); +assert(blk->throttle_node); +throttle_group_config(throttle_get_tgm(blk->throttle_node), cfg); } void blk_io_limits_disable(BlockBackend *blk) { BlockDriverState *bs, *throttle_node; -throttle_node = blk_get_public(blk)->throttle_node; +throttle_node = blk->throttle_node; assert(throttle_node); @@ -1903,7 +1898,7 @@ void blk_io_limits_disable(BlockBackend *blk) * blk, at this point it might have more than one parent, so use * bdrv_replace_node(). This destroys throttle_node */ bdrv_replace_node(throttle_node, bs, _abort); -blk_get_public(blk)->throttle_node = NULL; +blk->throttle_node = NULL; bdrv_unref(bs); bdrv_drained_end(bs); @@ -1944,7 +1939,7 @@ void blk_io_limits_enable(BlockBackend *blk, const char *group, Error **errp) end: bdrv_drained_end(bs); -blk_get_public(blk)->throttle_node = throttle_node; +blk->throttle_node = throttle_node; } void blk_io_limits_update_group(BlockBackend *blk, const char *group, Error **errp) @@ -1952,11 +1947,11 @@ void blk_io_limits_update_group(BlockBackend *blk, const char *group, Error **er ThrottleGroupMember *tgm; /* this BB is not part of any group */ -if (!blk->public.throttle_node) { +if (!blk->throttle_node) { return; } -tgm = throttle_get_tgm(blk->public.throttle_node); +tgm = throttle_get_tgm(blk->throttle_node); /* this BB is a part of the same group than the one we want */ if (!g_strcmp0(throttle_group_get_name(tgm), group)) { @@ -1981,8 +1976,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 (blk->public.throttle_node) { -tgm = throttle_get_tgm(blk->public.throttle_node); +if (blk->throttle_node) { +tgm = throttle_get_tgm(blk->throttle_node); if (atomic_fetch_inc(>io_limits_disabled) == 0) { throttle_group_restart_tgm(tgm); } @@ -1995,8 +1990,8 @@ static void blk_root_drained_end(BdrvChild *child) BlockBackend *blk = child->opaque; assert(blk->quiesce_counter); -if (blk->public.throttle_node) { -tgm = throttle_get_tgm(blk->public.throttle_node); +if (blk->throttle_node) { +tgm = throttle_get_tgm(blk->throttle_node); assert(tgm->io_li
Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
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 signature.asc Description: PGP signature
[Qemu-devel] [PATCH v4 4/7] block: convert ThrottleGroup to object with QOM
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 <el13...@mail.ntua.gr> --- v4 major changes: removed throttle_groups_lock changed reference counting of ThrottleGroup objects Notes: Note: I tested Markus Armbruster's patch in <87wp7fghi9@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 | 421 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, 626 insertions(+), 60 deletions(-) diff --git a/block/throttle-groups.c b/block/throttle-groups.c index f711a3dc62..751e86c676 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,12 +75,11 @@ typedef struct ThrottleGroup { bool any_timer_armed[2]; QEMUClockType clock_type; -/* These two are protected by the global throttle_groups_lock */ -unsigned refcount; +/* This field is protected by the global QEMU mutex */ QTAILQ_ENTRY(ThrottleGroup) list; } ThrottleGroup; -static QemuMutex throttle_groups_lock; +/* This is protected by the global QEMU mutex */ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = QTAILQ_HEAD_INITIALIZER(throttle_groups); @@ -77,7 +88,11 @@ 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 + * This function edits throttle_groups and must be called under the global + * mutex. + * + * @name: the name of t
[Qemu-devel] [PATCH v4 5/7] block: add throttle block filter driver
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 <el13...@mail.ntua.gr> --- block/Makefile.objs | 1 + block/throttle.c| 315 include/qemu/throttle-options.h | 1 + 3 files changed, 317 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 00..3e6cb1de7b --- /dev/null +++ b/block/throttle.c @@ -0,0 +1,315 @@ +/* + * 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. + * + * Returns -1 and sets errp if a burst_length value is over UINT_MAX. + */ +static int throttle_extract_options(QemuOpts *opts, ThrottleConfig *cfg, +Error **errp) +{ +#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, THROTTLE_OPT_PREFIX 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_BP
[Qemu-devel] [PATCH v4 3/7] block: tidy ThrottleGroupMember initializations
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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(>public.throttle_group_member.throttled_reqs_lock); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); -qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>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(>throttled_reqs_lock); +qemu_co_queue_init(>throttled_reqs[0]); +qemu_co_queue_init(>throttled_reqs[1]); qemu_mutex_unlock(>lock); } -- 2.11.0
[Qemu-devel] [PATCH v4 2/7] block: add aio_context field in ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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 = >public.throttle_group_member; if (bs) { -if (blk->public.throttle_group_member.throttle_state) { -tt = >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 = >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(>public.throttle_group_member, group); +throttle_group_register_tgm(>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..3b07b25f39 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, ); -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 = >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(>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(>
[Qemu-devel] [PATCH v4 7/7] block: add throttle block filter driver interface tests
Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- tests/qemu-iotests/184 | 310 + tests/qemu-iotests/184.out | 422 + tests/qemu-iotests/group | 1 + 3 files changed, 733 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 00..5c11d1123d --- /dev/null +++ b/tests/qemu-iotests/184 @@ -0,0 +1,310 @@ +#!/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 qcow2 +_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 <
[Qemu-devel] [PATCH v4 6/7] block: add BlockDevOptionsThrottle to QAPI
This is needed to configure throttle filter driver nodes with QAPI. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- qapi/block-core.json | 22 +- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 0bdc69aa5f..12fd749a94 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,24 @@ '*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. If not specified, an +#anonymous group will be created, which cannot be +#referenced by other throttle nodes. +# @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 +3174,7 @@ 'replication':'BlockdevOptionsReplication', 'sheepdog': 'BlockdevOptionsSheepdog', 'ssh':'BlockdevOptionsSsh', + 'throttle': 'BlockdevOptionsThrottle', 'vdi':'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat', -- 2.11.0
[Qemu-devel] [PATCH v4 1/7] block: move ThrottleGroup membership to ThrottleGroupMember
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: Alberto Garcia <be...@igalia.com> Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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(>public.throttled_reqs_lock); -qemu_co_queue_init(>public.throttled_reqs[0]); -qemu_co_queue_init(>public.throttled_reqs[1]); +qemu_co_mutex_init(>public.throttle_group_member.throttled_reqs_lock); +qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); +qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>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(>remove_bs_notifiers, blk); -if (blk->public.throttle_state) { -throttle_timers_detach_aio_context(>public.throttle_timers); +if (blk->public.throttle_group_member.throttle_state) { +tt = >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(>insert_bs_notifiers, blk); -if (blk->public.throttle_state) { +if (blk->public.throttle_group_member.throttle_state) { throttle_timers_attach_aio_context( ->public.throttle_timers, bdrv_get_aio_context(bs)); +>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(>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(>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(>public.throttle_timers); +if (blk->public.throttle_group_member.throttle_state) { +tt = >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(>public.throttle_timers, -
[Qemu-devel] [PATCH v4 0/7] add throttle block driver filter
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) v4: fix suggestions in block/throttle.c fix suggestions in block/throttle_groups.c add doc note in BlockDevOptionsThrottle 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 | 730 +--- block/throttle.c| 315 + 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| 70 +++- tests/qemu-iotests/184 | 310 + tests/qemu-iotests/184.out | 422 +++ tests/qemu-iotests/group| 1 + tests/test-throttle.c | 111 +++--- util/throttle.c | 151 + 16 files changed, 1995 insertions(+), 320 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
Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
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. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
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. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
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. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
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 <el13...@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. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
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 <el13...@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 signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
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 <el13...@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. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
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
Re: [Qemu-devel] [PATCH v3 5/7] block: add throttle block filter driver
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_OP
Re: [Qemu-devel] [PATCH 3/3] block: remove legacy I/O throttling
On Wed, Aug 02, 2017 at 12:33:19PM +0200, Kevin Wolf wrote: Am 02.08.2017 um 12:07 hat Stefan Hajnoczi geschrieben: On Tue, Aug 01, 2017 at 04:49:07PM +0300, Manos Pitsidianakis wrote: > diff --git a/block.c b/block.c > index 9ebdba28b0..c6aad25286 100644 > --- a/block.c > +++ b/block.c > @@ -1975,6 +1975,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, > child = g_new(BdrvChild, 1); > *child = (BdrvChild) { > .bs = NULL, > +.parent_bs = NULL, > .name = g_strdup(child_name), > .role = child_role, > .perm = perm, > @@ -2009,6 +2010,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, > if (child == NULL) { > return NULL; > } > +child->parent_bs = parent_bs; > > QLIST_INSERT_HEAD(_bs->children, child, next); > return child; > @@ -3729,6 +3731,12 @@ const char *bdrv_get_parent_name(const BlockDriverState *bs) > return name; > } > } > +if (c->parent_bs && c->parent_bs->implicit) { > +name = bdrv_get_parent_name(c->parent_bs); > +if (name && *name) { > +return name; > +} > +} > } > > return NULL; This should be a separate patch. Who updates parent_bs if the parent is changed (e.g. bdrv_replace_node())? We already have bs->parents. Why is BdrvChild->parent_bs needed? I haven't look at the whole patch yet, but BdrvChild->parent_bs is a thing that intentionally doesn't exist. A node simply has no business knowing its parents - which may or may not be BlockDriverStates (the obvious example where they aren't BDSes are BlockBackends, but block jobs own some BdrvChild objects, too). Usually the replacement is a BdrvChildRole callback. Kevin I think accessing the parent bs is necessary for the reasons I specified in my reply to Stefan. Will a callback that returns BdrvChild->opaque (parent_bs) for child_file and child_backing be okay? signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
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(_groups_lock); > > > > -/* Look for an existing group with that name */ > > -QTAILQ_FOREACH(iter, _groups, list) { > > -if (!strcmp(name, iter->name)) { > > -tg = iter; > > -break; > > +if (name) { > > +/* Look for an existing group with that name */ > > +QTAILQ_FOREACH(iter, _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(>lock); > > -throttle_init(>ts); > > -QLIST_INIT(>head); > > - > > -QTAILQ_INSERT_TAIL(_groups, tg, list); > > +throttle_group_obj_complete((UserCreatable *)tg, _abort); > > } > > > > -tg->refcount++; > > +qemu_mutex_lock(>l
Re: [Qemu-devel] [PATCH 3/3] block: remove legacy I/O throttling
On Wed, Aug 02, 2017 at 11:07:24AM +0100, Stefan Hajnoczi wrote: On Tue, Aug 01, 2017 at 04:49:07PM +0300, Manos Pitsidianakis wrote: diff --git a/block.c b/block.c index 9ebdba28b0..c6aad25286 100644 --- a/block.c +++ b/block.c @@ -1975,6 +1975,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, child = g_new(BdrvChild, 1); *child = (BdrvChild) { .bs = NULL, +.parent_bs = NULL, .name = g_strdup(child_name), .role = child_role, .perm = perm, @@ -2009,6 +2010,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, if (child == NULL) { return NULL; } +child->parent_bs = parent_bs; QLIST_INSERT_HEAD(_bs->children, child, next); return child; @@ -3729,6 +3731,12 @@ const char *bdrv_get_parent_name(const BlockDriverState *bs) return name; } } +if (c->parent_bs && c->parent_bs->implicit) { +name = bdrv_get_parent_name(c->parent_bs); +if (name && *name) { +return name; +} +} } return NULL; This should be a separate patch. Who updates parent_bs if the parent is changed (e.g. bdrv_replace_node())? We already have bs->parents. Why is BdrvChild->parent_bs needed? If I haven't misunderstood this, BdrvChild holds only the child part of the parent-child relationship and there's no way to access a parent from bs->parents. bdrv_replace_node() will thus only replace the child part in BdrvChild from the aspect of the parent. In the old child bs's perspective, one of the nodes of bs->parents is removed and in the new child bs's perspective a new node in bs->parents was inserted. parent_bs thus remains immutable. child->parent_bs is needed in this patch because in jobs if a job-ID is not specified the parent name is used, but this fails if the parent is an implicit node instead of BlockBackend and causes a regression (certain job setups suddenly need an explicit job ID instead of just working). -void blk_io_limits_disable(BlockBackend *blk) +void blk_io_limits_disable(BlockBackend *blk, Error **errp) { -assert(blk->public.throttle_group_member.throttle_state); -bdrv_drained_begin(blk_bs(blk)); Is it safe to drop drained_begin? We must ensure that no I/O requests run during this function. Thanks, I will put it back in. -throttle_group_unregister_tgm(>public.throttle_group_member); -bdrv_drained_end(blk_bs(blk)); +BlockDriverState *bs, *throttle_node; + +throttle_node = blk_get_public(blk)->throttle_node; Is blk_get_public() still necessary? Perhaps we can do away with the concept of the public struct now. It doesn't need to be done in this patch though. I can include a patch to move throttle_node to BlockBackend and remove all BlockBackendPublic code, is that okay? + +assert(throttle_node && throttle_node->refcnt == 1); Are you sure the throttle_node->refcnt == 1 assertion holds? For example, does the built-in NBD server have a reference to the throttle node if nbd-server-add is called after throttling has been enabled? Since we have the blk->throttle_node pointer we know we're the owner. Others may be using the node too but we may choose to remove it at any time. Hm.. If that's possible I guess we want the removal to be visible to the nbd server too. I will use bdrv_replace_node() instead. signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
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 <el13...@mail.ntua.gr> --- Notes: Note: I tested Markus Armbruster's patch in <87wp7fghi9@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 * cr
Re: [Qemu-devel] [RFC] block-insert-node and block-job-delete
On Tue, Aug 01, 2017 at 03:50:36PM +0200, Kevin Wolf wrote: Am 31.07.2017 um 19:30 hat Manos Pitsidianakis geschrieben: On Fri, Jul 28, 2017 at 02:08:43PM +0200, Kevin Wolf wrote: > Am 27.07.2017 um 12:07 hat Stefan Hajnoczi geschrieben: > > On Wed, Jul 26, 2017 at 09:23:20PM +0300, Manos Pitsidianakis wrote: > > > On Wed, Jul 26, 2017 at 04:12:21PM +0100, Stefan Hajnoczi wrote: > > > > On Wed, Jul 26, 2017 at 05:19:24PM +0300, Manos Pitsidianakis wrote: > > > > > This proposal follows a discussion we had with Kevin and Stefan on filter > > > > > node management. > > > > > > > > > > With block filter drivers arises a need to configure filter nodes on runtime > > > > > with QMP on live graphs. A problem with doing live graph modifications is > > > > > that some block jobs modify the graph when they are done and don't operate > > > > > under any synchronisation, resulting in a race condition if we try to insert > > > > > a filter node in the place of an existing edge. > > > > > > > > But maybe you are thinking about higher level race conditions between > > > > QMP commands. Can you give an example of the race? > > > > > > Exactly, synchronisation between the QMP/user actions. Here's an example, > > > correct me if I'm wrong: > > > > > > User issues a block-commit to this tree to commit A onto B: > > >BB -> A -> B > > > > > > This inserts the dummy mirror node at the top since this is a mirror job > > > (active commit) > > >BB -> dummy -> A -> B > > > > > > User wants to add a filter node F below the BB. First we create the node: > > >BB -> dummy -> A -> B > > > F / > > > > > > Commit finishes before we do block-insert-node, dummy and A is removed from > > > the BB's chain, mirror_exit calls bdrv_replace_node() > > > > > >BB > B > > >F -> / > > > > > > Now inserting F under BB will error since dummy does not exist any more. > > > > I see the diagrams but the flow isn't clear without the user's QMP > > commands. > > > > F is created using blockdev-add? What are the parameters and how does > > it know about dummy? I think this is an interesting question in itself > > because dummy is a transient internal node that QMP users don't > > necessarily know about. > > We can expect that users of block-insert-node also know about block job > filter nodes, simply because the former is newer than the latter. > > (I also don't like the name "dummy", this makes it sound like it's not > really a proper node. In reality, there is little reason to treat it > specially.) > > > What is the full block-insert-node command? > > > > > The proposal doesn't guard against the following: > > > User issues a block-commit to this tree to commit B onto C: > > >BB -> A -> B -> C > > > > > > The dummy node (commit's top bs is A): > > >BB -> A -> dummy -> B -> C > > > > > > blockdev-add of a filter we wish to eventually be between A and C: > > >BB -> A -> dummy -> B -> C > > > F/ > > > > > > if commit finishes before we issue block-insert-node (commit_complete() > > > calls bdrv_set_backing_hd() which only touches A) > > >BB -> A --> C > > > F -> dummy -> B / > > >resulting in error when issuing block-insert-node, > > > > > > else if we set the job to manual-delete and insert F: > > >BB -> A -> F -> dummy -> B -> C > > > deleting the job will result in F being lost since the job's top bs wasn't > > > updated. > > > > manual-delete is a solution for block jobs. The write threshold > > feature is a plain QMP command that in the future will create a > > transient internal node (before write notifier). > > Yes, that becomes a legacy QMP command then. The new way is blockdev-add > and block-insert-node for write threshold, too. > > Apart from that, a write threshold node never disappears by itself, it > is only inserted or removed in the context of a QMP command. That makes > it a lot less dangerous than automatic block completion. > > > I'm not sure it makes sense to turn the write threshold feature into a > > block job. I guess it could work, but it seems a little unnatural and > > maybe there will be other features
[Qemu-devel] [PATCH 3/3] block: remove legacy I/O throttling
This commit removes all I/O throttling from block/block-backend.c. In order to support the existing interface, it is changed to use the block/throttle.c filter driver. The throttle filter node that is created by the legacy interface is stored in a 'throttle_node' field in the BlockBackendPublic of the device. The legacy throttle node is managed by the legacy interface completely. More advanced configurations with the filter drive are possible using the QMP API, but these will be ignored by the legacy interface. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c | 8 +++ block/block-backend.c | 149 +--- block/qapi.c| 14 ++-- block/throttle.c| 8 +++ blockdev.c | 55 +++ include/block/block_int.h | 13 include/block/throttle-groups.h | 2 + include/sysemu/block-backend.h | 8 +-- tests/test-throttle.c | 15 ++-- 9 files changed, 186 insertions(+), 86 deletions(-) diff --git a/block.c b/block.c index 9ebdba28b0..c6aad25286 100644 --- a/block.c +++ b/block.c @@ -1975,6 +1975,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, child = g_new(BdrvChild, 1); *child = (BdrvChild) { .bs = NULL, +.parent_bs = NULL, .name = g_strdup(child_name), .role = child_role, .perm = perm, @@ -2009,6 +2010,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, if (child == NULL) { return NULL; } +child->parent_bs = parent_bs; QLIST_INSERT_HEAD(_bs->children, child, next); return child; @@ -3729,6 +3731,12 @@ const char *bdrv_get_parent_name(const BlockDriverState *bs) return name; } } +if (c->parent_bs && c->parent_bs->implicit) { +name = bdrv_get_parent_name(c->parent_bs); +if (name && *name) { +return name; +} +} } return NULL; diff --git a/block/block-backend.c b/block/block-backend.c index df0200fc49..45f45efee9 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -15,6 +15,7 @@ #include "block/block_int.h" #include "block/blockjob.h" #include "block/throttle-groups.h" +#include "qemu/throttle-options.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "qapi-event.h" @@ -282,8 +283,8 @@ static void blk_delete(BlockBackend *blk) assert(!blk->refcnt); assert(!blk->name); assert(!blk->dev); -if (blk->public.throttle_group_member.throttle_state) { -blk_io_limits_disable(blk); +if (blk->public.throttle_node) { +blk_io_limits_disable(blk, _abort); } if (blk->root) { blk_remove_bs(blk); @@ -593,13 +594,7 @@ BlockBackend *blk_by_public(BlockBackendPublic *public) */ void blk_remove_bs(BlockBackend *blk) { -ThrottleTimers *tt; - notifier_list_notify(>remove_bs_notifiers, blk); -if (blk->public.throttle_group_member.throttle_state) { -tt = >public.throttle_group_member.throttle_timers; -throttle_timers_detach_aio_context(tt); -} blk_update_root_state(blk); @@ -620,12 +615,6 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) bdrv_ref(bs); notifier_list_notify(>insert_bs_notifiers, blk); -if (blk->public.throttle_group_member.throttle_state) { -throttle_timers_attach_aio_context( ->public.throttle_group_member.throttle_timers, -bdrv_get_aio_context(bs)); -} - return 0; } @@ -983,13 +972,6 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, } bdrv_inc_in_flight(bs); - -/* throttling disk I/O */ -if (blk->public.throttle_group_member.throttle_state) { - throttle_group_co_io_limits_intercept(>public.throttle_group_member, -bytes, false); -} - ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); bdrv_dec_in_flight(bs); return ret; @@ -1010,11 +992,6 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, } bdrv_inc_in_flight(bs); -/* throttling disk I/O */ -if (blk->public.throttle_group_member.throttle_state) { - throttle_group_co_io_limits_intercept(>public.throttle_group_member, -bytes, true); -} if (!blk->enable_write_cache) { flags |= BDRV_REQ_FUA; @@ -1682,16 +1659,9 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) { BlockDriverState *bs = blk_bs(blk); -ThrottleGroupMember *tgm = >public.throttle_group_mem
[Qemu-devel] [PATCH 0/3] block: remove legacy I/O throttling
This series depends on my other series 'add throttle block driver filter' currently on v3, which as the name suggests adds a throttle filter driver. Replacing the current I/O interface means the user will use the same options as before and QEMU will create a hidden throttle filter node beneath the device's BlockBackend. Manos Pitsidianakis (3): block: add options parameter to bdrv_new_open_driver() block: skip implicit nodes in snapshots, blockjobs block: remove legacy I/O throttling block.c | 41 ++- block/block-backend.c | 149 +--- block/commit.c | 4 +- block/mirror.c | 2 +- block/qapi.c| 14 ++-- block/throttle.c| 8 +++ block/vvfat.c | 2 +- blockdev.c | 67 ++ include/block/block.h | 2 +- include/block/block_int.h | 15 include/block/throttle-groups.h | 2 + include/sysemu/block-backend.h | 8 +-- tests/test-throttle.c | 15 ++-- 13 files changed, 235 insertions(+), 94 deletions(-) -- 2.11.0
[Qemu-devel] [PATCH 1/3] block: add options parameter to bdrv_new_open_driver()
Allow passing a QDict *options parameter to bdrv_new_open_driver() so that it can be used if a driver needs it upon creation. The previous behaviour (empty bs->options and bs->explicit_options) remains when options is NULL. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c | 16 +--- block/commit.c| 4 ++-- block/mirror.c| 2 +- block/vvfat.c | 2 +- include/block/block.h | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/block.c b/block.c index 37e72b7a96..886a457ab0 100644 --- a/block.c +++ b/block.c @@ -1149,16 +1149,26 @@ free_and_fail: return ret; } +/* + * If options is not NULL it is cloned (which adds another reference to the + * option entries). If the call to bdrv_new_open_driver() is successful, the + * caller should unref options to pass ownership. + * */ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp) + int flags, QDict *options, Error **errp) { BlockDriverState *bs; int ret; bs = bdrv_new(); bs->open_flags = flags; -bs->explicit_options = qdict_new(); -bs->options = qdict_new(); +if (options) { +bs->explicit_options = qdict_clone_shallow(options); +bs->options = qdict_clone_shallow(options); +} else { +bs->explicit_options = qdict_new(); +bs->options = qdict_new(); +} bs->opaque = NULL; update_options_from_flags(bs->options, flags); diff --git a/block/commit.c b/block/commit.c index c7857c3321..539e23c3f8 100644 --- a/block/commit.c +++ b/block/commit.c @@ -342,7 +342,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, /* Insert commit_top block node above top, so we can block consistent read * on the backing chain below it */ commit_top_bs = bdrv_new_open_driver(_commit_top, filter_node_name, 0, - errp); + NULL, errp); if (commit_top_bs == NULL) { goto fail; } @@ -494,7 +494,7 @@ int bdrv_commit(BlockDriverState *bs) backing_file_bs = backing_bs(bs); commit_top_bs = bdrv_new_open_driver(_commit_top, NULL, BDRV_O_RDWR, - _err); + NULL, _err); if (commit_top_bs == NULL) { error_report_err(local_err); goto ro_cleanup; diff --git a/block/mirror.c b/block/mirror.c index c9a6a3ca86..e1a160e6ea 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1164,7 +1164,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, * reads on the top, while disabling it in the intermediate nodes, and make * the backing chain writable. */ mirror_top_bs = bdrv_new_open_driver(_mirror_top, filter_node_name, - BDRV_O_RDWR, errp); + BDRV_O_RDWR, NULL, errp); if (mirror_top_bs == NULL) { return; } diff --git a/block/vvfat.c b/block/vvfat.c index a9e207f7f0..6c59473baf 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3194,7 +3194,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) #endif backing = bdrv_new_open_driver(_write_target, NULL, BDRV_O_ALLOW_RDWR, - _abort); + NULL, _abort); *(void**) backing->opaque = s; bdrv_set_backing_hd(s->bs, backing, _abort); diff --git a/include/block/block.h b/include/block/block.h index 34770bb33a..020289a37d 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -263,7 +263,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp); BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, - int flags, Error **errp); + int flags, QDict *options, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, int flags); -- 2.11.0
[Qemu-devel] [PATCH 2/3] block: skip implicit nodes in snapshots, blockjobs
Implicit filter nodes added at the top of nodes can interfere with block jobs. This is not a problem when they are added by other jobs since adding another job will issue a QERR_DEVICE_IN_USE, but it can happen in the next commit which introduces an implicitly created throttle filter node below BlockBackend, which we want to be skipped during automatic operations on the graph since the user does not necessarily know about their existence. Signed-off-by: Manos Pitsidianakis <el13...@mail.ntua.gr> --- block.c | 17 + blockdev.c| 12 include/block/block_int.h | 2 ++ 3 files changed, 31 insertions(+) diff --git a/block.c b/block.c index 886a457ab0..9ebdba28b0 100644 --- a/block.c +++ b/block.c @@ -4947,3 +4947,20 @@ bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, return drv->bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp); } + +/* Get first non-implicit node down a bs chain. */ +BlockDriverState *bdrv_get_first_non_implicit(BlockDriverState *bs) +{ +if (!bs) { +return NULL; +} + +if (!bs->implicit) { +return bs; +} + +/* at this point bs is implicit and must have a child */ +assert(bs->file); + +return bdrv_get_first_non_implicit(bs->file->bs); +} diff --git a/blockdev.c b/blockdev.c index 23475abb72..d903a23786 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1664,6 +1664,9 @@ static void external_snapshot_prepare(BlkActionState *common, return; } +/* Skip implicit filter nodes */ +state->old_bs = bdrv_get_first_non_implicit(state->old_bs); + /* Acquire AioContext now so any threads operating on old_bs stop */ state->aio_context = bdrv_get_aio_context(state->old_bs); aio_context_acquire(state->aio_context); @@ -3095,6 +3098,9 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, return; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_non_implicit(bs); + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3209,6 +3215,9 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, return NULL; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_non_implicit(bs); + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3484,6 +3493,9 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) return; } +/* Skip implicit filter nodes */ +bs = bdrv_get_first_non_implicit(bs); + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); diff --git a/include/block/block_int.h b/include/block/block_int.h index d4f4ea7584..9eeae490f0 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -981,4 +981,6 @@ void bdrv_dec_in_flight(BlockDriverState *bs); void blockdev_close_all_bdrv_states(void); +BlockDriverState *bdrv_get_first_non_implicit(BlockDriverState *bs); + #endif /* BLOCK_INT_H */ -- 2.11.0
Re: [Qemu-devel] [RFC] block-insert-node and block-job-delete
On Fri, Jul 28, 2017 at 02:08:43PM +0200, Kevin Wolf wrote: Am 27.07.2017 um 12:07 hat Stefan Hajnoczi geschrieben: On Wed, Jul 26, 2017 at 09:23:20PM +0300, Manos Pitsidianakis wrote: > On Wed, Jul 26, 2017 at 04:12:21PM +0100, Stefan Hajnoczi wrote: > > On Wed, Jul 26, 2017 at 05:19:24PM +0300, Manos Pitsidianakis wrote: > > > This proposal follows a discussion we had with Kevin and Stefan on filter > > > node management. > > > > > > With block filter drivers arises a need to configure filter nodes on runtime > > > with QMP on live graphs. A problem with doing live graph modifications is > > > that some block jobs modify the graph when they are done and don't operate > > > under any synchronisation, resulting in a race condition if we try to insert > > > a filter node in the place of an existing edge. > > > > But maybe you are thinking about higher level race conditions between > > QMP commands. Can you give an example of the race? > > Exactly, synchronisation between the QMP/user actions. Here's an example, > correct me if I'm wrong: > > User issues a block-commit to this tree to commit A onto B: >BB -> A -> B > > This inserts the dummy mirror node at the top since this is a mirror job > (active commit) >BB -> dummy -> A -> B > > User wants to add a filter node F below the BB. First we create the node: >BB -> dummy -> A -> B > F / > > Commit finishes before we do block-insert-node, dummy and A is removed from > the BB's chain, mirror_exit calls bdrv_replace_node() > >BB > B >F -> / > > Now inserting F under BB will error since dummy does not exist any more. I see the diagrams but the flow isn't clear without the user's QMP commands. F is created using blockdev-add? What are the parameters and how does it know about dummy? I think this is an interesting question in itself because dummy is a transient internal node that QMP users don't necessarily know about. We can expect that users of block-insert-node also know about block job filter nodes, simply because the former is newer than the latter. (I also don't like the name "dummy", this makes it sound like it's not really a proper node. In reality, there is little reason to treat it specially.) What is the full block-insert-node command? > The proposal doesn't guard against the following: > User issues a block-commit to this tree to commit B onto C: >BB -> A -> B -> C > > The dummy node (commit's top bs is A): >BB -> A -> dummy -> B -> C > > blockdev-add of a filter we wish to eventually be between A and C: >BB -> A -> dummy -> B -> C > F/ > > if commit finishes before we issue block-insert-node (commit_complete() > calls bdrv_set_backing_hd() which only touches A) >BB -> A --> C > F -> dummy -> B / >resulting in error when issuing block-insert-node, > > else if we set the job to manual-delete and insert F: >BB -> A -> F -> dummy -> B -> C > deleting the job will result in F being lost since the job's top bs wasn't > updated. manual-delete is a solution for block jobs. The write threshold feature is a plain QMP command that in the future will create a transient internal node (before write notifier). Yes, that becomes a legacy QMP command then. The new way is blockdev-add and block-insert-node for write threshold, too. Apart from that, a write threshold node never disappears by itself, it is only inserted or removed in the context of a QMP command. That makes it a lot less dangerous than automatic block completion. I'm not sure it makes sense to turn the write threshold feature into a block job. I guess it could work, but it seems a little unnatural and maybe there will be other features that use transient internal nodes. Yeah, no reason to do so. Just a manually created filter node. Can you explain what you have in mind? The current workflow is using block-set-write-threshold on the targetted bs. If we want write threshold to be on node level we would want a new filter driver so that it can take options special to write-threshold. Unless we make the notify filter be a write threshold by default, and when using it internally by the backup job, disable the threshold and add our backup notifier to the node's list. Of course the current write threshold code could be refactored into a new driver so that it doesn't have to rely on notifiers. The way I've currently done the conversion is to add an implicit filter when enabling the threshold, just like in backup jobs. signature.asc Description: PGP signature
[Qemu-devel] [PATCH v3 1/7] block: move ThrottleGroup membership to ThrottleGroupMember
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 <stefa...@redhat.com> Signed-off-by: Manos Pitsidianakis <el13...@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(>public.throttled_reqs_lock); -qemu_co_queue_init(>public.throttled_reqs[0]); -qemu_co_queue_init(>public.throttled_reqs[1]); +qemu_co_mutex_init(>public.throttle_group_member.throttled_reqs_lock); +qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[0]); +qemu_co_queue_init(>public.throttle_group_member.throttled_reqs[1]); block_acct_init(>stats); notifier_list_init(>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(>remove_bs_notifiers, blk); -if (blk->public.throttle_state) { -throttle_timers_detach_aio_context(>public.throttle_timers); +if (blk->public.throttle_group_member.throttle_state) { +tt = >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(>insert_bs_notifiers, blk); -if (blk->public.throttle_state) { +if (blk->public.throttle_group_member.throttle_state) { throttle_timers_attach_aio_context( ->public.throttle_timers, bdrv_get_aio_context(bs)); +>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(>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(>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(>public.throttle_timers); +if (blk->public.throttle_group_member.throttle_state) { +tt = >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(>public.throttle_timers, - new_context);
[Qemu-devel] [PATCH v3 7/7] block: add throttle block filter driver interface tests
Signed-off-by: Manos Pitsidianakis <el13...@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 00..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 <
[Qemu-devel] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
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 <el13...@mail.ntua.gr> --- Notes: Note: I tested Markus Armbruster's patch in <87wp7fghi9@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(_groups_lock); -/* Look for an existing group with that name */ -QTAILQ_FOREACH(iter