mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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 <berto@igalia.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
committed by
Kevin Wolf
parent
64182a6b8b
commit
022cdc9f40
+37
-29
@@ -273,9 +273,9 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||
blk->shared_perm = shared_perm;
|
||||
blk_set_enable_write_cache(blk, true);
|
||||
|
||||
qemu_co_mutex_init(&blk->public.throttled_reqs_lock);
|
||||
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
|
||||
qemu_co_queue_init(&blk->public.throttled_reqs[1]);
|
||||
qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock);
|
||||
qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
|
||||
qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
|
||||
block_acct_init(&blk->stats);
|
||||
|
||||
notifier_list_init(&blk->remove_bs_notifiers);
|
||||
@@ -343,7 +343,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) {
|
||||
@@ -658,9 +658,12 @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
|
||||
*/
|
||||
void blk_remove_bs(BlockBackend *blk)
|
||||
{
|
||||
ThrottleTimers *tt;
|
||||
|
||||
notifier_list_notify(&blk->remove_bs_notifiers, blk);
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_timers_detach_aio_context(&blk->public.throttle_timers);
|
||||
if (blk->public.throttle_group_member.throttle_state) {
|
||||
tt = &blk->public.throttle_group_member.throttle_timers;
|
||||
throttle_timers_detach_aio_context(tt);
|
||||
}
|
||||
|
||||
blk_update_root_state(blk);
|
||||
@@ -682,9 +685,10 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
|
||||
bdrv_ref(bs);
|
||||
|
||||
notifier_list_notify(&blk->insert_bs_notifiers, blk);
|
||||
if (blk->public.throttle_state) {
|
||||
if (blk->public.throttle_group_member.throttle_state) {
|
||||
throttle_timers_attach_aio_context(
|
||||
&blk->public.throttle_timers, bdrv_get_aio_context(bs));
|
||||
&blk->public.throttle_group_member.throttle_timers,
|
||||
bdrv_get_aio_context(bs));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1046,8 +1050,9 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
|
||||
bdrv_inc_in_flight(bs);
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(blk, bytes, false);
|
||||
if (blk->public.throttle_group_member.throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
|
||||
bytes, false);
|
||||
}
|
||||
|
||||
ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags);
|
||||
@@ -1070,10 +1075,10 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
|
||||
}
|
||||
|
||||
bdrv_inc_in_flight(bs);
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(blk, bytes, true);
|
||||
if (blk->public.throttle_group_member.throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
|
||||
bytes, true);
|
||||
}
|
||||
|
||||
if (!blk->enable_write_cache) {
|
||||
@@ -1742,15 +1747,17 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
ThrottleTimers *tt;
|
||||
|
||||
if (bs) {
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_timers_detach_aio_context(&blk->public.throttle_timers);
|
||||
if (blk->public.throttle_group_member.throttle_state) {
|
||||
tt = &blk->public.throttle_group_member.throttle_timers;
|
||||
throttle_timers_detach_aio_context(tt);
|
||||
}
|
||||
bdrv_set_aio_context(bs, new_context);
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_timers_attach_aio_context(&blk->public.throttle_timers,
|
||||
new_context);
|
||||
if (blk->public.throttle_group_member.throttle_state) {
|
||||
tt = &blk->public.throttle_group_member.throttle_timers;
|
||||
throttle_timers_attach_aio_context(tt, new_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1969,33 +1976,34 @@ int blk_commit_all(void)
|
||||
/* throttling disk I/O limits */
|
||||
void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
|
||||
{
|
||||
throttle_group_config(blk, cfg);
|
||||
throttle_group_config(&blk->public.throttle_group_member, cfg);
|
||||
}
|
||||
|
||||
void blk_io_limits_disable(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->public.throttle_state);
|
||||
assert(blk->public.throttle_group_member.throttle_state);
|
||||
bdrv_drained_begin(blk_bs(blk));
|
||||
throttle_group_unregister_blk(blk);
|
||||
throttle_group_unregister_tgm(&blk->public.throttle_group_member);
|
||||
bdrv_drained_end(blk_bs(blk));
|
||||
}
|
||||
|
||||
/* should be called before blk_set_io_limits if a limit is set */
|
||||
void blk_io_limits_enable(BlockBackend *blk, const char *group)
|
||||
{
|
||||
assert(!blk->public.throttle_state);
|
||||
throttle_group_register_blk(blk, group);
|
||||
assert(!blk->public.throttle_group_member.throttle_state);
|
||||
throttle_group_register_tgm(&blk->public.throttle_group_member, group);
|
||||
}
|
||||
|
||||
void blk_io_limits_update_group(BlockBackend *blk, const char *group)
|
||||
{
|
||||
/* this BB is not part of any group */
|
||||
if (!blk->public.throttle_state) {
|
||||
if (!blk->public.throttle_group_member.throttle_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* this BB is a part of the same group than the one we want */
|
||||
if (!g_strcmp0(throttle_group_get_name(blk), group)) {
|
||||
if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member),
|
||||
group)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2017,8 +2025,8 @@ static void blk_root_drained_begin(BdrvChild *child)
|
||||
/* Note that blk->root may not be accessible here yet if we are just
|
||||
* attaching to a BlockDriverState that is drained. Use child instead. */
|
||||
|
||||
if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) {
|
||||
throttle_group_restart_blk(blk);
|
||||
if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) {
|
||||
throttle_group_restart_tgm(&blk->public.throttle_group_member);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2027,8 +2035,8 @@ static void blk_root_drained_end(BdrvChild *child)
|
||||
BlockBackend *blk = child->opaque;
|
||||
assert(blk->quiesce_counter);
|
||||
|
||||
assert(blk->public.io_limits_disabled);
|
||||
atomic_dec(&blk->public.io_limits_disabled);
|
||||
assert(blk->public.throttle_group_member.io_limits_disabled);
|
||||
atomic_dec(&blk->public.throttle_group_member.io_limits_disabled);
|
||||
|
||||
if (--blk->quiesce_counter == 0) {
|
||||
if (blk->dev_ops && blk->dev_ops->drained_end) {
|
||||
|
||||
+5
-3
@@ -66,10 +66,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
||||
|
||||
info->detect_zeroes = bs->detect_zeroes;
|
||||
|
||||
if (blk && blk_get_public(blk)->throttle_state) {
|
||||
if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) {
|
||||
ThrottleConfig cfg;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
|
||||
throttle_group_get_config(blk, &cfg);
|
||||
throttle_group_get_config(&blkp->throttle_group_member, &cfg);
|
||||
|
||||
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
||||
@@ -117,7 +118,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
||||
info->iops_size = cfg.op_size;
|
||||
|
||||
info->has_group = true;
|
||||
info->group = g_strdup(throttle_group_get_name(blk));
|
||||
info->group =
|
||||
g_strdup(throttle_group_get_name(&blkp->throttle_group_member));
|
||||
}
|
||||
|
||||
info->write_threshold = bdrv_write_threshold_get(bs);
|
||||
|
||||
+144
-144
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -2686,7 +2686,7 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
|
||||
if (throttle_enabled(&cfg)) {
|
||||
/* Enable I/O limits if they're not enabled yet, otherwise
|
||||
* just update the throttling group. */
|
||||
if (!blk_get_public(blk)->throttle_state) {
|
||||
if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
|
||||
blk_io_limits_enable(blk,
|
||||
arg->has_group ? arg->group :
|
||||
arg->has_device ? arg->device :
|
||||
@@ -2696,7 +2696,7 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
|
||||
}
|
||||
/* Set the new throttling configuration */
|
||||
blk_set_io_limits(blk, &cfg);
|
||||
} else if (blk_get_public(blk)->throttle_state) {
|
||||
} else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
|
||||
/* If all throttling settings are set to 0, disable I/O limits */
|
||||
blk_io_limits_disable(blk);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+29
-24
@@ -669,6 +669,7 @@ static void test_groups(void)
|
||||
ThrottleConfig cfg1, cfg2;
|
||||
BlockBackend *blk1, *blk2, *blk3;
|
||||
BlockBackendPublic *blkp1, *blkp2, *blkp3;
|
||||
ThrottleGroupMember *tgm1, *tgm2, *tgm3;
|
||||
|
||||
/* No actual I/O is performed on these devices */
|
||||
blk1 = blk_new(0, BLK_PERM_ALL);
|
||||
@@ -679,21 +680,25 @@ static void test_groups(void)
|
||||
blkp2 = blk_get_public(blk2);
|
||||
blkp3 = blk_get_public(blk3);
|
||||
|
||||
g_assert(blkp1->throttle_state == NULL);
|
||||
g_assert(blkp2->throttle_state == NULL);
|
||||
g_assert(blkp3->throttle_state == NULL);
|
||||
tgm1 = &blkp1->throttle_group_member;
|
||||
tgm2 = &blkp2->throttle_group_member;
|
||||
tgm3 = &blkp3->throttle_group_member;
|
||||
|
||||
throttle_group_register_blk(blk1, "bar");
|
||||
throttle_group_register_blk(blk2, "foo");
|
||||
throttle_group_register_blk(blk3, "bar");
|
||||
g_assert(tgm1->throttle_state == NULL);
|
||||
g_assert(tgm2->throttle_state == NULL);
|
||||
g_assert(tgm3->throttle_state == NULL);
|
||||
|
||||
g_assert(blkp1->throttle_state != NULL);
|
||||
g_assert(blkp2->throttle_state != NULL);
|
||||
g_assert(blkp3->throttle_state != NULL);
|
||||
throttle_group_register_tgm(tgm1, "bar");
|
||||
throttle_group_register_tgm(tgm2, "foo");
|
||||
throttle_group_register_tgm(tgm3, "bar");
|
||||
|
||||
g_assert(!strcmp(throttle_group_get_name(blk1), "bar"));
|
||||
g_assert(!strcmp(throttle_group_get_name(blk2), "foo"));
|
||||
g_assert(blkp1->throttle_state == blkp3->throttle_state);
|
||||
g_assert(tgm1->throttle_state != NULL);
|
||||
g_assert(tgm2->throttle_state != NULL);
|
||||
g_assert(tgm3->throttle_state != NULL);
|
||||
|
||||
g_assert(!strcmp(throttle_group_get_name(tgm1), "bar"));
|
||||
g_assert(!strcmp(throttle_group_get_name(tgm2), "foo"));
|
||||
g_assert(tgm1->throttle_state == tgm3->throttle_state);
|
||||
|
||||
/* Setting the config of a group member affects the whole group */
|
||||
throttle_config_init(&cfg1);
|
||||
@@ -701,29 +706,29 @@ static void test_groups(void)
|
||||
cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
|
||||
cfg1.buckets[THROTTLE_OPS_READ].avg = 20000;
|
||||
cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
|
||||
throttle_group_config(blk1, &cfg1);
|
||||
throttle_group_config(tgm1, &cfg1);
|
||||
|
||||
throttle_group_get_config(blk1, &cfg1);
|
||||
throttle_group_get_config(blk3, &cfg2);
|
||||
throttle_group_get_config(tgm1, &cfg1);
|
||||
throttle_group_get_config(tgm3, &cfg2);
|
||||
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
|
||||
|
||||
cfg2.buckets[THROTTLE_BPS_READ].avg = 4547;
|
||||
cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
|
||||
cfg2.buckets[THROTTLE_OPS_READ].avg = 123;
|
||||
cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
|
||||
throttle_group_config(blk3, &cfg1);
|
||||
throttle_group_config(tgm3, &cfg1);
|
||||
|
||||
throttle_group_get_config(blk1, &cfg1);
|
||||
throttle_group_get_config(blk3, &cfg2);
|
||||
throttle_group_get_config(tgm1, &cfg1);
|
||||
throttle_group_get_config(tgm3, &cfg2);
|
||||
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
|
||||
|
||||
throttle_group_unregister_blk(blk1);
|
||||
throttle_group_unregister_blk(blk2);
|
||||
throttle_group_unregister_blk(blk3);
|
||||
throttle_group_unregister_tgm(tgm1);
|
||||
throttle_group_unregister_tgm(tgm2);
|
||||
throttle_group_unregister_tgm(tgm3);
|
||||
|
||||
g_assert(blkp1->throttle_state == NULL);
|
||||
g_assert(blkp2->throttle_state == NULL);
|
||||
g_assert(blkp3->throttle_state == NULL);
|
||||
g_assert(tgm1->throttle_state == NULL);
|
||||
g_assert(tgm2->throttle_state == NULL);
|
||||
g_assert(tgm3->throttle_state == NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
||||
Reference in New Issue
Block a user