From a5614993d79584af93bb845f69f59872b3f76cf8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 21 Mar 2016 10:49:51 +0100 Subject: block: Make sure throttled BDSes always have a BB It was already true in principle that a throttled BDS always has a BB attached, except that the order of operations while attaching or detaching a BDS to/from a BB wasn't careful enough. This commit breaks graph manipulations while I/O throttling is enabled. It would have been possible to keep things working with some temporary hacks, but quite cumbersome, so it's not worth the hassle. We'll fix things again in a minute. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Alberto Garcia Acked-by: Stefan Hajnoczi --- tests/test-throttle.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/test-throttle.c b/tests/test-throttle.c index 744a524368..77b95d6380 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -574,11 +574,16 @@ static void test_accounting(void) static void test_groups(void) { ThrottleConfig cfg1, cfg2; + BlockBackend *blk1, *blk2, *blk3; BlockDriverState *bdrv1, *bdrv2, *bdrv3; - bdrv1 = bdrv_new(); - bdrv2 = bdrv_new(); - bdrv3 = bdrv_new(); + blk1 = blk_new_with_bs(&error_abort); + blk2 = blk_new_with_bs(&error_abort); + blk3 = blk_new_with_bs(&error_abort); + + bdrv1 = blk_bs(blk1); + bdrv2 = blk_bs(blk2); + bdrv3 = blk_bs(blk3); g_assert(bdrv1->throttle_state == NULL); g_assert(bdrv2->throttle_state == NULL); -- cgit 1.4.1 From 31dce3ccca98bc9f9eb57f8b08b008edd07661ba Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 21 Mar 2016 11:30:57 +0100 Subject: block: throttle-groups: Use BlockBackend pointers internally As a first step towards moving I/O throttling to the BlockBackend level, this patch changes all pointers in struct ThrottleGroup from referencing a BlockDriverState to referencing a BlockBackend. This change is valid because we made sure that throttling can only be enabled on BDSes which have a BB attached. Signed-off-by: Kevin Wolf Reviewed-by: Alberto Garcia Acked-by: Stefan Hajnoczi --- block/io.c | 4 +- block/throttle-groups.c | 134 ++++++++++++++++++++-------------------- include/block/block_int.h | 1 - include/block/throttle-groups.h | 4 +- include/sysemu/block-backend.h | 5 +- tests/test-throttle.c | 13 ++-- 6 files changed, 83 insertions(+), 78 deletions(-) (limited to 'tests') diff --git a/block/io.c b/block/io.c index cd6d71a503..ede74c5c03 100644 --- a/block/io.c +++ b/block/io.c @@ -70,7 +70,7 @@ void bdrv_io_limits_disable(BlockDriverState *bs) { assert(bs->throttle_state); bdrv_no_throttling_begin(bs); - throttle_group_unregister_bs(bs); + throttle_group_unregister_blk(bs->blk); bdrv_no_throttling_end(bs); } @@ -78,7 +78,7 @@ void bdrv_io_limits_disable(BlockDriverState *bs) void bdrv_io_limits_enable(BlockDriverState *bs, const char *group) { assert(!bs->throttle_state); - throttle_group_register_bs(bs, group); + throttle_group_register_blk(bs->blk, group); } void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group) diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 9ac063a0cd..1e6e2e5f4f 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "sysemu/block-backend.h" #include "block/throttle-groups.h" #include "qemu/queue.h" #include "qemu/thread.h" @@ -57,8 +58,8 @@ typedef struct ThrottleGroup { QemuMutex lock; /* This lock protects the following four fields */ ThrottleState ts; - QLIST_HEAD(, BlockDriverState) head; - BlockDriverState *tokens[2]; + QLIST_HEAD(, BlockBackendPublic) head; + BlockBackend *tokens[2]; bool any_timer_armed[2]; /* These two are protected by the global throttle_groups_lock */ @@ -145,81 +146,81 @@ const char *throttle_group_get_name(BlockDriverState *bs) return tg->name; } -/* Return the next BlockDriverState in the round-robin sequence, - * simulating a circular list. +/* Return the next BlockBackend in the round-robin sequence, simulating a + * circular list. * * This assumes that tg->lock is held. * - * @bs: the current BlockDriverState - * @ret: the next BlockDriverState in the sequence + * @blk: the current BlockBackend + * @ret: the next BlockBackend in the sequence */ -static BlockDriverState *throttle_group_next_bs(BlockDriverState *bs) +static BlockBackend *throttle_group_next_blk(BlockBackend *blk) { + BlockDriverState *bs = blk_bs(blk); ThrottleState *ts = bs->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); - BlockDriverState *next = QLIST_NEXT(bs, round_robin); + BlockBackendPublic *next = QLIST_NEXT(blk_get_public(blk), round_robin); if (!next) { - return QLIST_FIRST(&tg->head); + next = QLIST_FIRST(&tg->head); } - return next; + return blk_by_public(next); } -/* Return the next BlockDriverState in the round-robin sequence with - * pending I/O requests. +/* Return the next BlockBackend in the round-robin sequence with pending I/O + * requests. * * This assumes that tg->lock is held. * - * @bs: the current BlockDriverState + * @blk: the current BlockBackend * @is_write: the type of operation (read/write) - * @ret: the next BlockDriverState with pending requests, or bs - * if there is none. + * @ret: the next BlockBackend with pending requests, or blk if there is + * none. */ -static BlockDriverState *next_throttle_token(BlockDriverState *bs, - bool is_write) +static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) { - ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); - BlockDriverState *token, *start; + ThrottleGroup *tg = container_of(blk_bs(blk)->throttle_state, + ThrottleGroup, ts); + BlockBackend *token, *start; start = token = tg->tokens[is_write]; /* get next bs round in round robin style */ - token = throttle_group_next_bs(token); - while (token != start && !token->pending_reqs[is_write]) { - token = throttle_group_next_bs(token); + token = throttle_group_next_blk(token); + while (token != start && !blk_bs(token)->pending_reqs[is_write]) { + token = throttle_group_next_blk(token); } /* If no IO are queued for scheduling on the next round robin token * then decide the token is the current bs because chances are * the current bs get the current request queued. */ - if (token == start && !token->pending_reqs[is_write]) { - token = bs; + if (token == start && !blk_bs(token)->pending_reqs[is_write]) { + token = blk; } return token; } -/* Check if the next I/O request for a BlockDriverState needs to be - * throttled or not. If there's no timer set in this group, set one - * and update the token accordingly. +/* Check if the next I/O request for a BlockBackend needs to be throttled or + * not. If there's no timer set in this group, set one and update the token + * accordingly. * * This assumes that tg->lock is held. * - * @bs: the current BlockDriverState + * @blk: the current BlockBackend * @is_write: the type of operation (read/write) * @ret: whether the I/O request needs to be throttled or not */ -static bool throttle_group_schedule_timer(BlockDriverState *bs, - bool is_write) +static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write) { - ThrottleState *ts = bs->throttle_state; - ThrottleTimers *tt = &bs->throttle_timers; + ThrottleState *ts = blk_bs(blk)->throttle_state; + ThrottleTimers *tt = &blk_bs(blk)->throttle_timers; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); bool must_wait; - if (bs->io_limits_disabled) { + if (blk_bs(blk)->io_limits_disabled) { return false; } @@ -230,9 +231,9 @@ static bool throttle_group_schedule_timer(BlockDriverState *bs, must_wait = throttle_schedule_timer(ts, tt, is_write); - /* If a timer just got armed, set bs as the current token */ + /* If a timer just got armed, set blk as the current token */ if (must_wait) { - tg->tokens[is_write] = bs; + tg->tokens[is_write] = blk; tg->any_timer_armed[is_write] = true; } @@ -243,18 +244,19 @@ static bool throttle_group_schedule_timer(BlockDriverState *bs, * * This assumes that tg->lock is held. * - * @bs: the current BlockDriverState + * @blk: the current BlockBackend * @is_write: the type of operation (read/write) */ -static void schedule_next_request(BlockDriverState *bs, bool is_write) +static void schedule_next_request(BlockBackend *blk, bool is_write) { + BlockDriverState *bs = blk_bs(blk); ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); bool must_wait; - BlockDriverState *token; + BlockBackend *token; /* Check if there's any pending request to schedule next */ - token = next_throttle_token(bs, is_write); - if (!token->pending_reqs[is_write]) { + token = next_throttle_token(blk, is_write); + if (!blk_bs(token)->pending_reqs[is_write]) { return; } @@ -266,9 +268,9 @@ static void schedule_next_request(BlockDriverState *bs, bool is_write) /* Give preference to requests from the current bs */ if (qemu_in_coroutine() && qemu_co_queue_next(&bs->throttled_reqs[is_write])) { - token = bs; + token = blk; } else { - ThrottleTimers *tt = &token->throttle_timers; + ThrottleTimers *tt = &blk_bs(token)->throttle_timers; int64_t now = qemu_clock_get_ns(tt->clock_type); timer_mod(tt->timers[is_write], now + 1); tg->any_timer_armed[is_write] = true; @@ -290,13 +292,13 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, bool is_write) { bool must_wait; - BlockDriverState *token; + BlockBackend *token; ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); qemu_mutex_lock(&tg->lock); /* First we check if this I/O has to be throttled. */ - token = next_throttle_token(bs, is_write); + token = next_throttle_token(bs->blk, is_write); must_wait = throttle_group_schedule_timer(token, is_write); /* Wait if there's a timer set or queued requests of this type */ @@ -312,7 +314,7 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, throttle_account(bs->throttle_state, is_write, bytes); /* Schedule the next request */ - schedule_next_request(bs, is_write); + schedule_next_request(bs->blk, is_write); qemu_mutex_unlock(&tg->lock); } @@ -395,7 +397,7 @@ static void timer_cb(BlockDriverState *bs, bool is_write) * scheduling the next one */ if (empty_queue) { qemu_mutex_lock(&tg->lock); - schedule_next_request(bs, is_write); + schedule_next_request(bs->blk, is_write); qemu_mutex_unlock(&tg->lock); } } @@ -410,17 +412,17 @@ static void write_timer_cb(void *opaque) timer_cb(opaque, true); } -/* Register a BlockDriverState in the throttling group, also - * initializing its timers and updating its throttle_state pointer to - * point to it. If a throttling group with that name does not exist - * yet, it will be created. +/* Register a BlockBackend in the throttling group, also initializing its + * timers and updating its throttle_state pointer to point to it. If a + * throttling group with that name does not exist yet, it will be created. * - * @bs: the BlockDriverState to insert + * @blk: the BlockBackend to insert * @groupname: the name of the group */ -void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) +void throttle_group_register_blk(BlockBackend *blk, const char *groupname) { int i; + BlockDriverState *bs = blk_bs(blk); ThrottleState *ts = throttle_group_incref(groupname); ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); int clock_type = QEMU_CLOCK_REALTIME; @@ -433,14 +435,14 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) bs->throttle_state = ts; qemu_mutex_lock(&tg->lock); - /* If the ThrottleGroup is new set this BlockDriverState as the token */ + /* If the ThrottleGroup is new set this BlockBackend as the token */ for (i = 0; i < 2; i++) { if (!tg->tokens[i]) { - tg->tokens[i] = bs; + tg->tokens[i] = blk; } } - QLIST_INSERT_HEAD(&tg->head, bs, round_robin); + QLIST_INSERT_HEAD(&tg->head, blk_get_public(blk), round_robin); throttle_timers_init(&bs->throttle_timers, bdrv_get_aio_context(bs), @@ -452,19 +454,19 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) qemu_mutex_unlock(&tg->lock); } -/* Unregister a BlockDriverState from its group, removing it from the - * list, destroying the timers and setting the throttle_state pointer - * to NULL. +/* Unregister a BlockBackend from its group, removing it from the list, + * destroying the timers and setting the throttle_state pointer to NULL. * - * The BlockDriverState must not have pending throttled requests, so - * the caller has to drain them first. + * The BlockBackend must not have pending throttled requests, so the caller has + * to drain them first. * * The group will be destroyed if it's empty after this operation. * - * @bs: the BlockDriverState to remove + * @blk: the BlockBackend to remove */ -void throttle_group_unregister_bs(BlockDriverState *bs) +void throttle_group_unregister_blk(BlockBackend *blk) { + BlockDriverState *bs = blk_bs(blk); ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); int i; @@ -474,10 +476,10 @@ void throttle_group_unregister_bs(BlockDriverState *bs) qemu_mutex_lock(&tg->lock); for (i = 0; i < 2; i++) { - if (tg->tokens[i] == bs) { - BlockDriverState *token = throttle_group_next_bs(bs); + if (tg->tokens[i] == blk) { + BlockBackend *token = throttle_group_next_blk(blk); /* Take care of the case where this is the last bs in the group */ - if (token == bs) { + if (token == blk) { token = NULL; } tg->tokens[i] = token; @@ -485,7 +487,7 @@ void throttle_group_unregister_bs(BlockDriverState *bs) } /* remove the current bs from the list */ - QLIST_REMOVE(bs, round_robin); + QLIST_REMOVE(blk_get_public(blk), round_robin); throttle_timers_destroy(&bs->throttle_timers); qemu_mutex_unlock(&tg->lock); diff --git a/include/block/block_int.h b/include/block/block_int.h index a029c2003f..3f5d2b1ccc 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -435,7 +435,6 @@ struct BlockDriverState { ThrottleState *throttle_state; ThrottleTimers throttle_timers; unsigned pending_reqs[2]; - QLIST_ENTRY(BlockDriverState) round_robin; /* Offset after the highest byte written to */ uint64_t wr_highest_offset; diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index 395f72d444..b9114eedeb 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -36,8 +36,8 @@ void throttle_group_unref(ThrottleState *ts); void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg); void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg); -void throttle_group_register_bs(BlockDriverState *bs, const char *groupname); -void throttle_group_unregister_bs(BlockDriverState *bs); +void throttle_group_register_blk(BlockBackend *blk, const char *groupname); +void throttle_group_unregister_blk(BlockBackend *blk); void throttle_group_restart_bs(BlockDriverState *bs); void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index a771603bd0..1dcd70e2ca 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -63,7 +63,10 @@ typedef struct BlockDevOps { * 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 */ typedef struct BlockBackendPublic { - int dummy; /* empty structs are illegal */ + /* I/O throttling */ + /* The following fields are protected by the ThrottleGroup lock. + * See the ThrottleGroup documentation for details. */ + QLIST_ENTRY(BlockBackendPublic) round_robin; } BlockBackendPublic; BlockBackend *blk_new(Error **errp); diff --git a/tests/test-throttle.c b/tests/test-throttle.c index 77b95d6380..beaa2a3518 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -20,6 +20,7 @@ #include "qemu/throttle.h" #include "qemu/error-report.h" #include "block/throttle-groups.h" +#include "sysemu/block-backend.h" static AioContext *ctx; static LeakyBucket bkt; @@ -589,9 +590,9 @@ static void test_groups(void) g_assert(bdrv2->throttle_state == NULL); g_assert(bdrv3->throttle_state == NULL); - throttle_group_register_bs(bdrv1, "bar"); - throttle_group_register_bs(bdrv2, "foo"); - throttle_group_register_bs(bdrv3, "bar"); + throttle_group_register_blk(blk1, "bar"); + throttle_group_register_blk(blk2, "foo"); + throttle_group_register_blk(blk3, "bar"); g_assert(bdrv1->throttle_state != NULL); g_assert(bdrv2->throttle_state != NULL); @@ -623,9 +624,9 @@ static void test_groups(void) throttle_group_get_config(bdrv3, &cfg2); g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); - throttle_group_unregister_bs(bdrv1); - throttle_group_unregister_bs(bdrv2); - throttle_group_unregister_bs(bdrv3); + throttle_group_unregister_blk(blk1); + throttle_group_unregister_blk(blk2); + throttle_group_unregister_blk(blk3); g_assert(bdrv1->throttle_state == NULL); g_assert(bdrv2->throttle_state == NULL); -- cgit 1.4.1 From 49d2165d7d6b589d1ea28b15a8874c417bdc55ed Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 21 Mar 2016 11:58:21 +0100 Subject: block: Convert throttle_group_get_name() to BlockBackend Signed-off-by: Kevin Wolf Reviewed-by: Alberto Garcia Acked-by: Stefan Hajnoczi --- block/block-backend.c | 2 +- block/io.c | 2 +- block/qapi.c | 2 +- block/throttle-groups.c | 12 ++++++------ include/block/throttle-groups.h | 2 +- tests/test-throttle.c | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/block/block-backend.c b/block/block-backend.c index 2f8acbdbaf..964a205d38 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1525,7 +1525,7 @@ void blk_update_root_state(BlockBackend *blk) throttle_group_unref(blk->root_state.throttle_state); } if (blk->root->bs->throttle_state) { - const char *name = throttle_group_get_name(blk->root->bs); + const char *name = throttle_group_get_name(blk); blk->root_state.throttle_group = g_strdup(name); blk->root_state.throttle_state = throttle_group_incref(name); } else { diff --git a/block/io.c b/block/io.c index ede74c5c03..f6fb86883a 100644 --- a/block/io.c +++ b/block/io.c @@ -89,7 +89,7 @@ void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group) } /* this bs is a part of the same group than the one we want */ - if (!g_strcmp0(throttle_group_get_name(bs), group)) { + if (!g_strcmp0(throttle_group_get_name(bs->blk), group)) { return; } diff --git a/block/qapi.c b/block/qapi.c index c5f6ba643c..a3e514d89b 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -118,7 +118,7 @@ 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(bs)); + info->group = g_strdup(throttle_group_get_name(bs->blk)); } info->write_threshold = bdrv_write_threshold_get(bs); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 1e6e2e5f4f..e50ccaaf7e 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -133,16 +133,16 @@ void throttle_group_unref(ThrottleState *ts) qemu_mutex_unlock(&throttle_groups_lock); } -/* Get the name from a BlockDriverState's ThrottleGroup. The name (and - * the pointer) is guaranteed to remain constant during the lifetime - * of the group. +/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer) + * is guaranteed to remain constant during the lifetime of the group. * - * @bs: a BlockDriverState that is member of a throttling group + * @blk: a BlockBackend that is member of a throttling group * @ret: the name of the group. */ -const char *throttle_group_get_name(BlockDriverState *bs) +const char *throttle_group_get_name(BlockBackend *blk) { - ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + ThrottleGroup *tg = container_of(blk_bs(blk)->throttle_state, + ThrottleGroup, ts); return tg->name; } diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index b9114eedeb..bd55a34194 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -28,7 +28,7 @@ #include "qemu/throttle.h" #include "block/block_int.h" -const char *throttle_group_get_name(BlockDriverState *bs); +const char *throttle_group_get_name(BlockBackend *blk); ThrottleState *throttle_group_incref(const char *name); void throttle_group_unref(ThrottleState *ts); diff --git a/tests/test-throttle.c b/tests/test-throttle.c index beaa2a3518..1a322f1897 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -598,8 +598,8 @@ static void test_groups(void) g_assert(bdrv2->throttle_state != NULL); g_assert(bdrv3->throttle_state != NULL); - g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar")); - g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo")); + g_assert(!strcmp(throttle_group_get_name(blk1), "bar")); + g_assert(!strcmp(throttle_group_get_name(blk2), "foo")); g_assert(bdrv1->throttle_state == bdrv3->throttle_state); /* Setting the config of a group member affects the whole group */ -- cgit 1.4.1 From 27ccdd52598290f0f8b58be56e235aff7aebfaf3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 21 Mar 2016 12:56:44 +0100 Subject: block: Move throttling fields from BDS to BB This patch changes where the throttling state is stored (used to be the BlockDriverState, now it is the BlockBackend), but it doesn't actually make it a BB level feature yet. For example, throttling is still disabled when the BDS is detached from the BB. Signed-off-by: Kevin Wolf Acked-by: Stefan Hajnoczi --- block.c | 22 +++---- block/block-backend.c | 13 ++-- block/io.c | 36 +++++++---- block/qapi.c | 2 +- block/throttle-groups.c | 129 +++++++++++++++++++++------------------- blockdev.c | 4 +- include/block/block_int.h | 13 ---- include/block/throttle-groups.h | 2 +- include/sysemu/block-backend.h | 11 +++- tests/test-throttle.c | 28 +++++---- 10 files changed, 142 insertions(+), 118 deletions(-) (limited to 'tests') diff --git a/block.c b/block.c index a7898449ac..f723060deb 100644 --- a/block.c +++ b/block.c @@ -237,8 +237,6 @@ BlockDriverState *bdrv_new(void) QLIST_INIT(&bs->op_blockers[i]); } notifier_with_return_list_init(&bs->before_write_notifiers); - qemu_co_queue_init(&bs->throttled_reqs[0]); - qemu_co_queue_init(&bs->throttled_reqs[1]); bs->refcnt = 1; bs->aio_context = qemu_get_aio_context(); @@ -1525,7 +1523,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, return -ENODEV; } - if (bs->throttle_state) { + if (bs->blk && blk_get_public(bs->blk)->throttle_state) { error_setg(errp, "Cannot reference an existing block device for " "which I/O throttling is enabled"); return -EINVAL; @@ -2124,7 +2122,7 @@ static void bdrv_close(BlockDriverState *bs) assert(!bs->job); /* Disable I/O limits and drain all pending throttled requests */ - if (bs->throttle_state) { + if (bs->blk && blk_get_public(bs->blk)->throttle_state) { bdrv_io_limits_disable(bs); } @@ -2257,8 +2255,8 @@ static void swap_feature_fields(BlockDriverState *bs_top, bdrv_move_feature_fields(bs_top, bs_new); bdrv_move_feature_fields(bs_new, &tmp); - assert(!bs_new->throttle_state); - if (bs_top->throttle_state) { + assert(!bs_new->blk); + if (bs_top->blk && blk_get_public(bs_top->blk)->throttle_state) { /* * FIXME Need to break I/O throttling with graph manipulations * temporarily because of conflicting invariants (3. will go away when @@ -2300,11 +2298,11 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) assert(!bdrv_requests_pending(bs_new)); bdrv_ref(bs_top); - change_parent_backing_link(bs_top, bs_new); /* Some fields always stay on top of the backing file chain */ swap_feature_fields(bs_top, bs_new); + change_parent_backing_link(bs_top, bs_new); bdrv_set_backing_hd(bs_new, bs_top); bdrv_unref(bs_top); @@ -3676,8 +3674,9 @@ void bdrv_detach_aio_context(BlockDriverState *bs) baf->detach_aio_context(baf->opaque); } - if (bs->throttle_state) { - throttle_timers_detach_aio_context(&bs->throttle_timers); + if (bs->blk && blk_get_public(bs->blk)->throttle_state) { + throttle_timers_detach_aio_context( + &blk_get_public(bs->blk)->throttle_timers); } if (bs->drv->bdrv_detach_aio_context) { bs->drv->bdrv_detach_aio_context(bs); @@ -3712,8 +3711,9 @@ void bdrv_attach_aio_context(BlockDriverState *bs, if (bs->drv->bdrv_attach_aio_context) { bs->drv->bdrv_attach_aio_context(bs, new_context); } - if (bs->throttle_state) { - throttle_timers_attach_aio_context(&bs->throttle_timers, new_context); + if (bs->blk && blk_get_public(bs->blk)->throttle_state) { + throttle_timers_attach_aio_context( + &blk_get_public(bs->blk)->throttle_timers, new_context); } QLIST_FOREACH(ban, &bs->aio_notifiers, list) { diff --git a/block/block-backend.c b/block/block-backend.c index 964a205d38..6880659665 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -107,8 +107,12 @@ BlockBackend *blk_new(Error **errp) blk = g_new0(BlockBackend, 1); blk->refcnt = 1; + qemu_co_queue_init(&blk->public.throttled_reqs[0]); + qemu_co_queue_init(&blk->public.throttled_reqs[1]); + notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers); + QTAILQ_INSERT_TAIL(&block_backends, blk, link); return blk; } @@ -437,7 +441,7 @@ void blk_remove_bs(BlockBackend *blk) notifier_list_notify(&blk->remove_bs_notifiers, blk); blk_update_root_state(blk); - if (blk->root->bs->throttle_state) { + if (blk->public.throttle_state) { bdrv_io_limits_disable(blk->root->bs); } @@ -795,7 +799,6 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf, int count) { - BlockDriverState *bs = blk_bs(blk); int ret; ret = blk_check_byte_request(blk, offset, count); @@ -803,9 +806,9 @@ int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf, return ret; } - bdrv_no_throttling_begin(bs); + bdrv_no_throttling_begin(blk_bs(blk)); ret = blk_pread(blk, offset, buf, count); - bdrv_no_throttling_end(bs); + bdrv_no_throttling_end(blk_bs(blk)); return ret; } @@ -1524,7 +1527,7 @@ void blk_update_root_state(BlockBackend *blk) g_free(blk->root_state.throttle_group); throttle_group_unref(blk->root_state.throttle_state); } - if (blk->root->bs->throttle_state) { + if (blk->public.throttle_state) { const char *name = throttle_group_get_name(blk); blk->root_state.throttle_group = g_strdup(name); blk->root_state.throttle_state = throttle_group_incref(name); diff --git a/block/io.c b/block/io.c index f6fb86883a..bdbaa1c0da 100644 --- a/block/io.c +++ b/block/io.c @@ -55,20 +55,31 @@ void bdrv_set_io_limits(BlockDriverState *bs, void bdrv_no_throttling_begin(BlockDriverState *bs) { - if (bs->io_limits_disabled++ == 0) { - throttle_group_restart_bs(bs); + if (!bs->blk) { + return; + } + + if (blk_get_public(bs->blk)->io_limits_disabled++ == 0) { + throttle_group_restart_blk(bs->blk); } } void bdrv_no_throttling_end(BlockDriverState *bs) { - assert(bs->io_limits_disabled); - --bs->io_limits_disabled; + BlockBackendPublic *blkp; + + if (!bs->blk) { + return; + } + + blkp = blk_get_public(bs->blk); + assert(blkp->io_limits_disabled); + --blkp->io_limits_disabled; } void bdrv_io_limits_disable(BlockDriverState *bs) { - assert(bs->throttle_state); + assert(blk_get_public(bs->blk)->throttle_state); bdrv_no_throttling_begin(bs); throttle_group_unregister_blk(bs->blk); bdrv_no_throttling_end(bs); @@ -77,14 +88,16 @@ void bdrv_io_limits_disable(BlockDriverState *bs) /* should be called before bdrv_set_io_limits if a limit is set */ void bdrv_io_limits_enable(BlockDriverState *bs, const char *group) { - assert(!bs->throttle_state); + BlockBackendPublic *blkp = blk_get_public(bs->blk); + + assert(!blkp->throttle_state); throttle_group_register_blk(bs->blk, group); } void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group) { /* this bs is not part of any group */ - if (!bs->throttle_state) { + if (!blk_get_public(bs->blk)->throttle_state) { return; } @@ -178,14 +191,15 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs) bool bdrv_requests_pending(BlockDriverState *bs) { BdrvChild *child; + BlockBackendPublic *blkp = bs->blk ? blk_get_public(bs->blk) : NULL; if (!QLIST_EMPTY(&bs->tracked_requests)) { return true; } - if (!qemu_co_queue_empty(&bs->throttled_reqs[0])) { + if (blkp && !qemu_co_queue_empty(&blkp->throttled_reqs[0])) { return true; } - if (!qemu_co_queue_empty(&bs->throttled_reqs[1])) { + if (blkp && !qemu_co_queue_empty(&blkp->throttled_reqs[1])) { return true; } @@ -1070,7 +1084,7 @@ int coroutine_fn bdrv_co_preadv(BlockDriverState *bs, } /* throttling disk I/O */ - if (bs->throttle_state) { + if (bs->blk && blk_get_public(bs->blk)->throttle_state) { throttle_group_co_io_limits_intercept(bs, bytes, false); } @@ -1431,7 +1445,7 @@ int coroutine_fn bdrv_co_pwritev(BlockDriverState *bs, } /* throttling disk I/O */ - if (bs->throttle_state) { + if (bs->blk && blk_get_public(bs->blk)->throttle_state) { throttle_group_co_io_limits_intercept(bs, bytes, true); } diff --git a/block/qapi.c b/block/qapi.c index a3e514d89b..1e4bb8a0a5 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -67,7 +67,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->backing_file_depth = bdrv_get_backing_file_depth(bs); info->detect_zeroes = bs->detect_zeroes; - if (bs->throttle_state) { + if (bs->blk && blk_get_public(bs->blk)->throttle_state) { ThrottleConfig cfg; throttle_group_get_config(bs, &cfg); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index e50ccaaf7e..56dc311867 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -30,7 +30,7 @@ #include "sysemu/qtest.h" /* The ThrottleGroup structure (with its ThrottleState) is shared - * among different BlockDriverState and it's independent from + * among different BlockBackends and it's independent from * AioContext, so in order to use it from different threads it needs * its own locking. * @@ -40,18 +40,18 @@ * The whole ThrottleGroup structure is private and invisible to * outside users, that only use it through its ThrottleState. * - * In addition to the ThrottleGroup structure, BlockDriverState has + * In addition to the ThrottleGroup structure, BlockBackendPublic has * fields that need to be accessed by other members of the group and - * therefore also need to be protected by this lock. Once a BDS is - * registered in a group those fields can be accessed by other threads - * any time. + * therefore also need to be protected by this lock. Once a + * BlockBackend is registered in a group those fields can be accessed + * by other threads any time. * * Again, all this is handled internally and is mostly transparent to * the outside. The 'throttle_timers' field however has an additional * constraint because it may be temporarily invalid (see for example * bdrv_set_aio_context()). Therefore in this file a thread will - * access some other BDS's timers only after verifying that that BDS - * has throttled requests in the queue. + * access some other BlockBackend's timers only after verifying that + * that BlockBackend has throttled requests in the queue. */ typedef struct ThrottleGroup { char *name; /* This is constant during the lifetime of the group */ @@ -141,8 +141,8 @@ void throttle_group_unref(ThrottleState *ts) */ const char *throttle_group_get_name(BlockBackend *blk) { - ThrottleGroup *tg = container_of(blk_bs(blk)->throttle_state, - ThrottleGroup, ts); + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); return tg->name; } @@ -156,10 +156,10 @@ const char *throttle_group_get_name(BlockBackend *blk) */ static BlockBackend *throttle_group_next_blk(BlockBackend *blk) { - BlockDriverState *bs = blk_bs(blk); - ThrottleState *ts = bs->throttle_state; + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleState *ts = blkp->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); - BlockBackendPublic *next = QLIST_NEXT(blk_get_public(blk), round_robin); + BlockBackendPublic *next = QLIST_NEXT(blkp, round_robin); if (!next) { next = QLIST_FIRST(&tg->head); @@ -180,15 +180,15 @@ static BlockBackend *throttle_group_next_blk(BlockBackend *blk) */ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) { - ThrottleGroup *tg = container_of(blk_bs(blk)->throttle_state, - ThrottleGroup, ts); + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); BlockBackend *token, *start; start = token = tg->tokens[is_write]; /* get next bs round in round robin style */ token = throttle_group_next_blk(token); - while (token != start && !blk_bs(token)->pending_reqs[is_write]) { + while (token != start && !blkp->pending_reqs[is_write]) { token = throttle_group_next_blk(token); } @@ -196,7 +196,7 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) * then decide the token is the current bs because chances are * the current bs get the current request queued. */ - if (token == start && !blk_bs(token)->pending_reqs[is_write]) { + if (token == start && !blkp->pending_reqs[is_write]) { token = blk; } @@ -215,12 +215,13 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) */ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write) { - ThrottleState *ts = blk_bs(blk)->throttle_state; - ThrottleTimers *tt = &blk_bs(blk)->throttle_timers; + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleState *ts = blkp->throttle_state; + ThrottleTimers *tt = &blkp->throttle_timers; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); bool must_wait; - if (blk_bs(blk)->io_limits_disabled) { + if (blkp->io_limits_disabled) { return false; } @@ -249,14 +250,14 @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write) */ static void schedule_next_request(BlockBackend *blk, bool is_write) { - BlockDriverState *bs = blk_bs(blk); - ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); bool must_wait; BlockBackend *token; /* Check if there's any pending request to schedule next */ token = next_throttle_token(blk, is_write); - if (!blk_bs(token)->pending_reqs[is_write]) { + if (!blkp->pending_reqs[is_write]) { return; } @@ -265,12 +266,12 @@ static void schedule_next_request(BlockBackend *blk, bool is_write) /* If it doesn't have to wait, queue it for immediate execution */ if (!must_wait) { - /* Give preference to requests from the current bs */ + /* Give preference to requests from the current blk */ if (qemu_in_coroutine() && - qemu_co_queue_next(&bs->throttled_reqs[is_write])) { + qemu_co_queue_next(&blkp->throttled_reqs[is_write])) { token = blk; } else { - ThrottleTimers *tt = &blk_bs(token)->throttle_timers; + ThrottleTimers *tt = &blkp->throttle_timers; int64_t now = qemu_clock_get_ns(tt->clock_type); timer_mod(tt->timers[is_write], now + 1); tg->any_timer_armed[is_write] = true; @@ -294,37 +295,40 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, bool must_wait; BlockBackend *token; - ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + BlockBackend *blk = bs->blk; + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); qemu_mutex_lock(&tg->lock); /* First we check if this I/O has to be throttled. */ - token = next_throttle_token(bs->blk, is_write); + token = next_throttle_token(blk, is_write); must_wait = throttle_group_schedule_timer(token, is_write); /* Wait if there's a timer set or queued requests of this type */ - if (must_wait || bs->pending_reqs[is_write]) { - bs->pending_reqs[is_write]++; + if (must_wait || blkp->pending_reqs[is_write]) { + blkp->pending_reqs[is_write]++; qemu_mutex_unlock(&tg->lock); - qemu_co_queue_wait(&bs->throttled_reqs[is_write]); + qemu_co_queue_wait(&blkp->throttled_reqs[is_write]); qemu_mutex_lock(&tg->lock); - bs->pending_reqs[is_write]--; + blkp->pending_reqs[is_write]--; } /* The I/O will be executed, so do the accounting */ - throttle_account(bs->throttle_state, is_write, bytes); + throttle_account(blkp->throttle_state, is_write, bytes); /* Schedule the next request */ - schedule_next_request(bs->blk, is_write); + schedule_next_request(blk, is_write); qemu_mutex_unlock(&tg->lock); } -void throttle_group_restart_bs(BlockDriverState *bs) +void throttle_group_restart_blk(BlockBackend *blk) { + BlockBackendPublic *blkp = blk_get_public(blk); int i; for (i = 0; i < 2; i++) { - while (qemu_co_enter_next(&bs->throttled_reqs[i])) { + while (qemu_co_enter_next(&blkp->throttled_reqs[i])) { ; } } @@ -339,8 +343,9 @@ void throttle_group_restart_bs(BlockDriverState *bs) */ void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) { - ThrottleTimers *tt = &bs->throttle_timers; - ThrottleState *ts = bs->throttle_state; + BlockBackendPublic *blkp = blk_get_public(bs->blk); + ThrottleTimers *tt = &blkp->throttle_timers; + ThrottleState *ts = blkp->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); qemu_mutex_lock(&tg->lock); /* throttle_config() cancels the timers */ @@ -353,8 +358,8 @@ void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) throttle_config(ts, tt, cfg); qemu_mutex_unlock(&tg->lock); - qemu_co_enter_next(&bs->throttled_reqs[0]); - qemu_co_enter_next(&bs->throttled_reqs[1]); + qemu_co_enter_next(&blkp->throttled_reqs[0]); + qemu_co_enter_next(&blkp->throttled_reqs[1]); } /* Get the throttle configuration from a particular group. Similar to @@ -366,7 +371,8 @@ void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) */ void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg) { - ThrottleState *ts = bs->throttle_state; + BlockBackendPublic *blkp = blk_get_public(bs->blk); + ThrottleState *ts = blkp->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); qemu_mutex_lock(&tg->lock); throttle_get_config(ts, cfg); @@ -376,12 +382,13 @@ void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg) /* ThrottleTimers callback. This wakes up a request that was waiting * because it had been throttled. * - * @bs: the BlockDriverState whose request had been throttled + * @blk: the BlockBackend whose request had been throttled * @is_write: the type of operation (read/write) */ -static void timer_cb(BlockDriverState *bs, bool is_write) +static void timer_cb(BlockBackend *blk, bool is_write) { - ThrottleState *ts = bs->throttle_state; + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleState *ts = blkp->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); bool empty_queue; @@ -391,13 +398,13 @@ static void timer_cb(BlockDriverState *bs, bool is_write) qemu_mutex_unlock(&tg->lock); /* Run the request that was waiting for this timer */ - empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]); + empty_queue = !qemu_co_enter_next(&blkp->throttled_reqs[is_write]); /* If the request queue was empty then we have to take care of * scheduling the next one */ if (empty_queue) { qemu_mutex_lock(&tg->lock); - schedule_next_request(bs->blk, is_write); + schedule_next_request(blk, is_write); qemu_mutex_unlock(&tg->lock); } } @@ -422,7 +429,7 @@ static void write_timer_cb(void *opaque) void throttle_group_register_blk(BlockBackend *blk, const char *groupname) { int i; - BlockDriverState *bs = blk_bs(blk); + BlockBackendPublic *blkp = blk_get_public(blk); ThrottleState *ts = throttle_group_incref(groupname); ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); int clock_type = QEMU_CLOCK_REALTIME; @@ -432,7 +439,7 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname) clock_type = QEMU_CLOCK_VIRTUAL; } - bs->throttle_state = ts; + blkp->throttle_state = ts; qemu_mutex_lock(&tg->lock); /* If the ThrottleGroup is new set this BlockBackend as the token */ @@ -442,14 +449,14 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname) } } - QLIST_INSERT_HEAD(&tg->head, blk_get_public(blk), round_robin); + QLIST_INSERT_HEAD(&tg->head, blkp, round_robin); - throttle_timers_init(&bs->throttle_timers, - bdrv_get_aio_context(bs), + throttle_timers_init(&blkp->throttle_timers, + blk_get_aio_context(blk), clock_type, read_timer_cb, write_timer_cb, - bs); + blk); qemu_mutex_unlock(&tg->lock); } @@ -466,19 +473,19 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname) */ void throttle_group_unregister_blk(BlockBackend *blk) { - BlockDriverState *bs = blk_bs(blk); - ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + BlockBackendPublic *blkp = blk_get_public(blk); + ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); int i; - assert(bs->pending_reqs[0] == 0 && bs->pending_reqs[1] == 0); - assert(qemu_co_queue_empty(&bs->throttled_reqs[0])); - assert(qemu_co_queue_empty(&bs->throttled_reqs[1])); + assert(blkp->pending_reqs[0] == 0 && blkp->pending_reqs[1] == 0); + assert(qemu_co_queue_empty(&blkp->throttled_reqs[0])); + assert(qemu_co_queue_empty(&blkp->throttled_reqs[1])); qemu_mutex_lock(&tg->lock); for (i = 0; i < 2; i++) { if (tg->tokens[i] == blk) { BlockBackend *token = throttle_group_next_blk(blk); - /* Take care of the case where this is the last bs in the group */ + /* Take care of the case where this is the last blk in the group */ if (token == blk) { token = NULL; } @@ -486,13 +493,13 @@ void throttle_group_unregister_blk(BlockBackend *blk) } } - /* remove the current bs from the list */ - QLIST_REMOVE(blk_get_public(blk), round_robin); - throttle_timers_destroy(&bs->throttle_timers); + /* remove the current blk from the list */ + QLIST_REMOVE(blkp, round_robin); + throttle_timers_destroy(&blkp->throttle_timers); qemu_mutex_unlock(&tg->lock); throttle_group_unref(&tg->ts); - bs->throttle_state = NULL; + blkp->throttle_state = NULL; } static void throttle_groups_init(void) diff --git a/blockdev.c b/blockdev.c index 8106ca7654..3211a40615 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2726,14 +2726,14 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, if (throttle_enabled(&cfg)) { /* Enable I/O limits if they're not enabled yet, otherwise * just update the throttling group. */ - if (!bs->throttle_state) { + if (!blk_get_public(bs->blk)->throttle_state) { bdrv_io_limits_enable(bs, has_group ? group : device); } else if (has_group) { bdrv_io_limits_update_group(bs, group); } /* Set the new throttling configuration */ bdrv_set_io_limits(bs, &cfg); - } else if (bs->throttle_state) { + } else if (blk_get_public(bs->blk)->throttle_state) { /* If all throttling settings are set to 0, disable I/O limits */ bdrv_io_limits_disable(bs); } diff --git a/include/block/block_int.h b/include/block/block_int.h index 3f5d2b1ccc..2bbc2c0609 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -26,7 +26,6 @@ #include "block/accounting.h" #include "block/block.h" -#include "block/throttle-groups.h" #include "qemu/option.h" #include "qemu/queue.h" #include "qemu/coroutine.h" @@ -424,18 +423,6 @@ struct BlockDriverState { /* number of in-flight serialising requests */ unsigned int serialising_in_flight; - /* I/O throttling. - * throttle_state tells us if this BDS has I/O limits configured. - * io_limits_disabled tells us if they are currently being enforced */ - CoQueue throttled_reqs[2]; - unsigned int io_limits_disabled; - - /* The following fields are protected by the ThrottleGroup lock. - * See the ThrottleGroup documentation for details. */ - ThrottleState *throttle_state; - ThrottleTimers throttle_timers; - unsigned pending_reqs[2]; - /* Offset after the highest byte written to */ uint64_t wr_highest_offset; diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index bd55a34194..840ba443ae 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -38,7 +38,7 @@ void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg); void throttle_group_register_blk(BlockBackend *blk, const char *groupname); void throttle_group_unregister_blk(BlockBackend *blk); -void throttle_group_restart_bs(BlockDriverState *bs); +void throttle_group_restart_blk(BlockBackend *blk); void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, unsigned int bytes, diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 1dcd70e2ca..08d27a86be 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -14,6 +14,7 @@ #define BLOCK_BACKEND_H #include "qemu/iov.h" +#include "block/throttle-groups.h" /* * TODO Have to include block/block.h for a bunch of block layer @@ -63,9 +64,17 @@ typedef struct BlockDevOps { * 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 */ typedef struct BlockBackendPublic { - /* I/O throttling */ + /* I/O throttling. + * throttle_state tells us if this BlockBackend has I/O limits configured. + * io_limits_disabled tells us if they are currently being enforced */ + CoQueue throttled_reqs[2]; + unsigned int io_limits_disabled; + /* The following fields are protected by the ThrottleGroup lock. * See the ThrottleGroup documentation for details. */ + ThrottleState *throttle_state; + ThrottleTimers throttle_timers; + unsigned pending_reqs[2]; QLIST_ENTRY(BlockBackendPublic) round_robin; } BlockBackendPublic; diff --git a/tests/test-throttle.c b/tests/test-throttle.c index 1a322f1897..a0200686d6 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -576,31 +576,35 @@ static void test_groups(void) { ThrottleConfig cfg1, cfg2; BlockBackend *blk1, *blk2, *blk3; - BlockDriverState *bdrv1, *bdrv2, *bdrv3; + BlockBackendPublic *blkp1, *blkp2, *blkp3; + BlockDriverState *bdrv1, *bdrv3; blk1 = blk_new_with_bs(&error_abort); blk2 = blk_new_with_bs(&error_abort); blk3 = blk_new_with_bs(&error_abort); bdrv1 = blk_bs(blk1); - bdrv2 = blk_bs(blk2); bdrv3 = blk_bs(blk3); - g_assert(bdrv1->throttle_state == NULL); - g_assert(bdrv2->throttle_state == NULL); - g_assert(bdrv3->throttle_state == NULL); + blkp1 = blk_get_public(blk1); + 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); throttle_group_register_blk(blk1, "bar"); throttle_group_register_blk(blk2, "foo"); throttle_group_register_blk(blk3, "bar"); - g_assert(bdrv1->throttle_state != NULL); - g_assert(bdrv2->throttle_state != NULL); - g_assert(bdrv3->throttle_state != NULL); + g_assert(blkp1->throttle_state != NULL); + g_assert(blkp2->throttle_state != NULL); + g_assert(blkp3->throttle_state != NULL); g_assert(!strcmp(throttle_group_get_name(blk1), "bar")); g_assert(!strcmp(throttle_group_get_name(blk2), "foo")); - g_assert(bdrv1->throttle_state == bdrv3->throttle_state); + g_assert(blkp1->throttle_state == blkp3->throttle_state); /* Setting the config of a group member affects the whole group */ throttle_config_init(&cfg1); @@ -628,9 +632,9 @@ static void test_groups(void) throttle_group_unregister_blk(blk2); throttle_group_unregister_blk(blk3); - g_assert(bdrv1->throttle_state == NULL); - g_assert(bdrv2->throttle_state == NULL); - g_assert(bdrv3->throttle_state == NULL); + g_assert(blkp1->throttle_state == NULL); + g_assert(blkp2->throttle_state == NULL); + g_assert(blkp3->throttle_state == NULL); } int main(int argc, char **argv) -- cgit 1.4.1 From 97148076e8beebbcab11e5cb581d8508722143fc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 21 Mar 2016 13:53:52 +0100 Subject: block: Move I/O throttling configuration functions to BlockBackend Signed-off-by: Kevin Wolf Reviewed-by: Alberto Garcia Acked-by: Stefan Hajnoczi --- block.c | 2 +- block/block-backend.c | 43 +++++++++++++++++++++++++++++++++++++++-- block/io.c | 41 --------------------------------------- block/qapi.c | 2 +- block/throttle-groups.c | 12 ++++++------ blockdev.c | 16 +++++++-------- include/block/block.h | 4 ---- include/block/block_int.h | 3 +-- include/block/throttle-groups.h | 4 ++-- include/sysemu/block-backend.h | 5 +++++ tests/test-throttle.c | 16 ++++++--------- 11 files changed, 71 insertions(+), 77 deletions(-) (limited to 'tests') diff --git a/block.c b/block.c index f723060deb..b5c02c4654 100644 --- a/block.c +++ b/block.c @@ -2123,7 +2123,7 @@ static void bdrv_close(BlockDriverState *bs) /* Disable I/O limits and drain all pending throttled requests */ if (bs->blk && blk_get_public(bs->blk)->throttle_state) { - bdrv_io_limits_disable(bs); + blk_io_limits_disable(bs->blk); } bdrv_drained_begin(bs); /* complete I/O */ diff --git a/block/block-backend.c b/block/block-backend.c index 730b8a9949..b9aaa604a6 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -442,7 +442,7 @@ void blk_remove_bs(BlockBackend *blk) blk_update_root_state(blk); if (blk->public.throttle_state) { - bdrv_io_limits_disable(blk->root->bs); + blk_io_limits_disable(blk); } blk->root->bs->blk = NULL; @@ -1556,7 +1556,7 @@ void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs) { bs->detect_zeroes = blk->root_state.detect_zeroes; if (blk->root_state.throttle_group) { - bdrv_io_limits_enable(bs, blk->root_state.throttle_group); + blk_io_limits_enable(blk, blk->root_state.throttle_group); } } @@ -1620,3 +1620,42 @@ int blk_flush_all(void) return result; } + + +/* throttling disk I/O limits */ +void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) +{ + throttle_group_config(blk, cfg); +} + +void blk_io_limits_disable(BlockBackend *blk) +{ + assert(blk->public.throttle_state); + bdrv_no_throttling_begin(blk_bs(blk)); + throttle_group_unregister_blk(blk); + bdrv_no_throttling_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); +} + +void blk_io_limits_update_group(BlockBackend *blk, const char *group) +{ + /* this BB is not part of any group */ + if (!blk->public.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)) { + return; + } + + /* need to change the group this bs belong to */ + blk_io_limits_disable(blk); + blk_io_limits_enable(blk, group); +} diff --git a/block/io.c b/block/io.c index cf2ac4cca5..1699f1ef18 100644 --- a/block/io.c +++ b/block/io.c @@ -46,13 +46,6 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque); static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags); -/* throttling disk I/O limits */ -void bdrv_set_io_limits(BlockDriverState *bs, - ThrottleConfig *cfg) -{ - throttle_group_config(bs, cfg); -} - void bdrv_no_throttling_begin(BlockDriverState *bs) { if (!bs->blk) { @@ -77,40 +70,6 @@ void bdrv_no_throttling_end(BlockDriverState *bs) --blkp->io_limits_disabled; } -void bdrv_io_limits_disable(BlockDriverState *bs) -{ - assert(blk_get_public(bs->blk)->throttle_state); - bdrv_no_throttling_begin(bs); - throttle_group_unregister_blk(bs->blk); - bdrv_no_throttling_end(bs); -} - -/* should be called before bdrv_set_io_limits if a limit is set */ -void bdrv_io_limits_enable(BlockDriverState *bs, const char *group) -{ - BlockBackendPublic *blkp = blk_get_public(bs->blk); - - assert(!blkp->throttle_state); - throttle_group_register_blk(bs->blk, group); -} - -void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group) -{ - /* this bs is not part of any group */ - if (!blk_get_public(bs->blk)->throttle_state) { - return; - } - - /* this bs is a part of the same group than the one we want */ - if (!g_strcmp0(throttle_group_get_name(bs->blk), group)) { - return; - } - - /* need to change the group this bs belong to */ - bdrv_io_limits_disable(bs); - bdrv_io_limits_enable(bs, group); -} - void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; diff --git a/block/qapi.c b/block/qapi.c index 1e4bb8a0a5..b0d8d6b71e 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -70,7 +70,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, if (bs->blk && blk_get_public(bs->blk)->throttle_state) { ThrottleConfig cfg; - throttle_group_get_config(bs, &cfg); + throttle_group_get_config(bs->blk, &cfg); info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 3db8cf74a5..59545e287e 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -337,12 +337,12 @@ void throttle_group_restart_blk(BlockBackend *blk) * to throttle_config(), but guarantees atomicity within the * throttling group. * - * @bs: a BlockDriverState that is member of the group + * @blk: a BlockBackend that is a member of the group * @cfg: the configuration to set */ -void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) +void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg) { - BlockBackendPublic *blkp = blk_get_public(bs->blk); + BlockBackendPublic *blkp = blk_get_public(blk); ThrottleTimers *tt = &blkp->throttle_timers; ThrottleState *ts = blkp->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); @@ -365,12 +365,12 @@ void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) * throttle_get_config(), but guarantees atomicity within the * throttling group. * - * @bs: a BlockDriverState that is member of the group + * @blk: a BlockBackend that is a member of the group * @cfg: the configuration will be written here */ -void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg) +void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg) { - BlockBackendPublic *blkp = blk_get_public(bs->blk); + BlockBackendPublic *blkp = blk_get_public(blk); ThrottleState *ts = blkp->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); qemu_mutex_lock(&tg->lock); diff --git a/blockdev.c b/blockdev.c index 3211a40615..89768ab759 100644 --- a/blockdev.c +++ b/blockdev.c @@ -616,8 +616,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, if (!throttling_group) { throttling_group = blk_name(blk); } - bdrv_io_limits_enable(bs, throttling_group); - bdrv_set_io_limits(bs, &cfg); + blk_io_limits_enable(blk, throttling_group); + blk_set_io_limits(blk, &cfg); } if (bdrv_key_required(bs)) { @@ -2726,16 +2726,16 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, if (throttle_enabled(&cfg)) { /* Enable I/O limits if they're not enabled yet, otherwise * just update the throttling group. */ - if (!blk_get_public(bs->blk)->throttle_state) { - bdrv_io_limits_enable(bs, has_group ? group : device); + if (!blk_get_public(blk)->throttle_state) { + blk_io_limits_enable(blk, has_group ? group : device); } else if (has_group) { - bdrv_io_limits_update_group(bs, group); + blk_io_limits_update_group(blk, group); } /* Set the new throttling configuration */ - bdrv_set_io_limits(bs, &cfg); - } else if (blk_get_public(bs->blk)->throttle_state) { + blk_set_io_limits(blk, &cfg); + } else if (blk_get_public(blk)->throttle_state) { /* If all throttling settings are set to 0, disable I/O limits */ - bdrv_io_limits_disable(bs); + blk_io_limits_disable(blk); } out: diff --git a/include/block/block.h b/include/block/block.h index b210832778..2c5c280a42 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -187,10 +187,6 @@ void bdrv_stats_print(Monitor *mon, const QObject *data); void bdrv_info_stats(Monitor *mon, QObject **ret_data); /* disk I/O throttling */ -void bdrv_io_limits_enable(BlockDriverState *bs, const char *group); -void bdrv_io_limits_disable(BlockDriverState *bs); -void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group); - void bdrv_init(void); void bdrv_init_with_whitelist(void); bool bdrv_uses_whitelist(void); diff --git a/include/block/block_int.h b/include/block/block_int.h index 2bbc2c0609..1218857f82 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -525,8 +525,7 @@ int get_tmp_filename(char *filename, int size); BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, const char *filename); -void bdrv_set_io_limits(BlockDriverState *bs, - ThrottleConfig *cfg); +bool bdrv_start_throttled_reqs(BlockDriverState *bs); /** diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index ac4224835b..d983d34074 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -33,8 +33,8 @@ const char *throttle_group_get_name(BlockBackend *blk); ThrottleState *throttle_group_incref(const char *name); void throttle_group_unref(ThrottleState *ts); -void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg); -void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg); +void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg); +void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg); void throttle_group_register_blk(BlockBackend *blk, const char *groupname); void throttle_group_unregister_blk(BlockBackend *blk); diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 08d27a86be..dd9c8ca4e0 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -212,4 +212,9 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque, int ret); +void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); +void blk_io_limits_disable(BlockBackend *blk); +void blk_io_limits_enable(BlockBackend *blk, const char *group); +void blk_io_limits_update_group(BlockBackend *blk, const char *group); + #endif diff --git a/tests/test-throttle.c b/tests/test-throttle.c index a0200686d6..5ec966c8a4 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -577,15 +577,11 @@ static void test_groups(void) ThrottleConfig cfg1, cfg2; BlockBackend *blk1, *blk2, *blk3; BlockBackendPublic *blkp1, *blkp2, *blkp3; - BlockDriverState *bdrv1, *bdrv3; blk1 = blk_new_with_bs(&error_abort); blk2 = blk_new_with_bs(&error_abort); blk3 = blk_new_with_bs(&error_abort); - bdrv1 = blk_bs(blk1); - bdrv3 = blk_bs(blk3); - blkp1 = blk_get_public(blk1); blkp2 = blk_get_public(blk2); blkp3 = blk_get_public(blk3); @@ -612,20 +608,20 @@ 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(bdrv1, &cfg1); + throttle_group_config(blk1, &cfg1); - throttle_group_get_config(bdrv1, &cfg1); - throttle_group_get_config(bdrv3, &cfg2); + throttle_group_get_config(blk1, &cfg1); + throttle_group_get_config(blk3, &cfg2); g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); cfg2.buckets[THROTTLE_BPS_READ].avg = 4547; cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349; cfg2.buckets[THROTTLE_OPS_READ].avg = 123; cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86; - throttle_group_config(bdrv3, &cfg1); + throttle_group_config(blk3, &cfg1); - throttle_group_get_config(bdrv1, &cfg1); - throttle_group_get_config(bdrv3, &cfg2); + throttle_group_get_config(blk1, &cfg1); + throttle_group_get_config(blk3, &cfg2); g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); throttle_group_unregister_blk(blk1); -- cgit 1.4.1 From 91c6e4b7bba906cfb8d84481da6340957f876c90 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 26 Feb 2016 13:50:43 +0100 Subject: block: Remove bdrv_aio_multiwrite() Since virtio-blk implements request merging itself these days, the only remaining users are test cases for the function. That doesn't make the function exactly useful any more. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Eric Blake --- block/block-backend.c | 14 --- block/io.c | 194 --------------------------------------- include/block/block.h | 7 +- include/sysemu/block-backend.h | 1 - qemu-io-cmds.c | 203 ----------------------------------------- tests/qemu-iotests/100 | 152 ------------------------------ tests/qemu-iotests/100.out | 103 --------------------- tests/qemu-iotests/136 | 20 +--- tests/qemu-iotests/136.out | 4 +- tests/qemu-iotests/group | 2 +- trace-events | 2 - 11 files changed, 9 insertions(+), 693 deletions(-) delete mode 100755 tests/qemu-iotests/100 delete mode 100644 tests/qemu-iotests/100.out (limited to 'tests') diff --git a/block/block-backend.c b/block/block-backend.c index a31fc204cd..8d6fc77b26 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1067,20 +1067,6 @@ void blk_aio_cancel_async(BlockAIOCB *acb) bdrv_aio_cancel_async(acb); } -int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs) -{ - int i, ret; - - for (i = 0; i < num_reqs; i++) { - ret = blk_check_request(blk, reqs[i].sector, reqs[i].nb_sectors); - if (ret < 0) { - return ret; - } - } - - return bdrv_aio_multiwrite(blk_bs(blk), reqs, num_reqs); -} - int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { if (!blk_is_available(blk)) { diff --git a/block/io.c b/block/io.c index 6e90805019..8a6f470478 100644 --- a/block/io.c +++ b/block/io.c @@ -1878,200 +1878,6 @@ BlockAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs, cb, opaque, true); } - -typedef struct MultiwriteCB { - int error; - int num_requests; - int num_callbacks; - struct { - BlockCompletionFunc *cb; - void *opaque; - QEMUIOVector *free_qiov; - } callbacks[]; -} MultiwriteCB; - -static void multiwrite_user_cb(MultiwriteCB *mcb) -{ - int i; - - for (i = 0; i < mcb->num_callbacks; i++) { - mcb->callbacks[i].cb(mcb->callbacks[i].opaque, mcb->error); - if (mcb->callbacks[i].free_qiov) { - qemu_iovec_destroy(mcb->callbacks[i].free_qiov); - } - g_free(mcb->callbacks[i].free_qiov); - } -} - -static void multiwrite_cb(void *opaque, int ret) -{ - MultiwriteCB *mcb = opaque; - - trace_multiwrite_cb(mcb, ret); - - if (ret < 0 && !mcb->error) { - mcb->error = ret; - } - - mcb->num_requests--; - if (mcb->num_requests == 0) { - multiwrite_user_cb(mcb); - g_free(mcb); - } -} - -static int multiwrite_req_compare(const void *a, const void *b) -{ - const BlockRequest *req1 = a, *req2 = b; - - /* - * Note that we can't simply subtract req2->sector from req1->sector - * here as that could overflow the return value. - */ - if (req1->sector > req2->sector) { - return 1; - } else if (req1->sector < req2->sector) { - return -1; - } else { - return 0; - } -} - -/* - * Takes a bunch of requests and tries to merge them. Returns the number of - * requests that remain after merging. - */ -static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs, - int num_reqs, MultiwriteCB *mcb) -{ - int i, outidx; - - // Sort requests by start sector - qsort(reqs, num_reqs, sizeof(*reqs), &multiwrite_req_compare); - - // Check if adjacent requests touch the same clusters. If so, combine them, - // filling up gaps with zero sectors. - outidx = 0; - for (i = 1; i < num_reqs; i++) { - int merge = 0; - int64_t oldreq_last = reqs[outidx].sector + reqs[outidx].nb_sectors; - - // Handle exactly sequential writes and overlapping writes. - if (reqs[i].sector <= oldreq_last) { - merge = 1; - } - - if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > - bs->bl.max_iov) { - merge = 0; - } - - if (bs->bl.max_transfer_length && reqs[outidx].nb_sectors + - reqs[i].nb_sectors > bs->bl.max_transfer_length) { - merge = 0; - } - - if (merge) { - size_t size; - QEMUIOVector *qiov = g_malloc0(sizeof(*qiov)); - qemu_iovec_init(qiov, - reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1); - - // Add the first request to the merged one. If the requests are - // overlapping, drop the last sectors of the first request. - size = (reqs[i].sector - reqs[outidx].sector) << 9; - qemu_iovec_concat(qiov, reqs[outidx].qiov, 0, size); - - // We should need to add any zeros between the two requests - assert (reqs[i].sector <= oldreq_last); - - // Add the second request - qemu_iovec_concat(qiov, reqs[i].qiov, 0, reqs[i].qiov->size); - - // Add tail of first request, if necessary - if (qiov->size < reqs[outidx].qiov->size) { - qemu_iovec_concat(qiov, reqs[outidx].qiov, qiov->size, - reqs[outidx].qiov->size - qiov->size); - } - - reqs[outidx].nb_sectors = qiov->size >> 9; - reqs[outidx].qiov = qiov; - - mcb->callbacks[i].free_qiov = reqs[outidx].qiov; - } else { - outidx++; - reqs[outidx].sector = reqs[i].sector; - reqs[outidx].nb_sectors = reqs[i].nb_sectors; - reqs[outidx].qiov = reqs[i].qiov; - } - } - - if (bs->blk) { - block_acct_merge_done(blk_get_stats(bs->blk), BLOCK_ACCT_WRITE, - num_reqs - outidx - 1); - } - - return outidx + 1; -} - -/* - * Submit multiple AIO write requests at once. - * - * On success, the function returns 0 and all requests in the reqs array have - * been submitted. In error case this function returns -1, and any of the - * requests may or may not be submitted yet. In particular, this means that the - * callback will be called for some of the requests, for others it won't. The - * caller must check the error field of the BlockRequest to wait for the right - * callbacks (if error != 0, no callback will be called). - * - * The implementation may modify the contents of the reqs array, e.g. to merge - * requests. However, the fields opaque and error are left unmodified as they - * are used to signal failure for a single request to the caller. - */ -int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs) -{ - MultiwriteCB *mcb; - int i; - - /* don't submit writes if we don't have a medium */ - if (bs->drv == NULL) { - for (i = 0; i < num_reqs; i++) { - reqs[i].error = -ENOMEDIUM; - } - return -1; - } - - if (num_reqs == 0) { - return 0; - } - - // Create MultiwriteCB structure - mcb = g_malloc0(sizeof(*mcb) + num_reqs * sizeof(*mcb->callbacks)); - mcb->num_requests = 0; - mcb->num_callbacks = num_reqs; - - for (i = 0; i < num_reqs; i++) { - mcb->callbacks[i].cb = reqs[i].cb; - mcb->callbacks[i].opaque = reqs[i].opaque; - } - - // Check for mergable requests - num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb); - - trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs); - - /* Run the aio requests. */ - mcb->num_requests = num_reqs; - for (i = 0; i < num_reqs; i++) { - bdrv_co_aio_rw_vector(bs, reqs[i].sector, reqs[i].qiov, - reqs[i].nb_sectors, reqs[i].flags, - multiwrite_cb, mcb, - true); - } - - return 0; -} - void bdrv_aio_cancel(BlockAIOCB *acb) { qemu_aio_ref(acb); diff --git a/include/block/block.h b/include/block/block.h index 2c5c280a42..d1f938064a 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -329,7 +329,7 @@ void bdrv_aio_cancel(BlockAIOCB *acb); void bdrv_aio_cancel_async(BlockAIOCB *acb); typedef struct BlockRequest { - /* Fields to be filled by multiwrite caller */ + /* Fields to be filled by caller */ union { struct { int64_t sector; @@ -345,13 +345,10 @@ typedef struct BlockRequest { BlockCompletionFunc *cb; void *opaque; - /* Filled by multiwrite implementation */ + /* Filled by block layer */ int error; } BlockRequest; -int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, - int num_reqs); - /* sg packet commands */ int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf); BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index dd9c8ca4e0..79f39b889e 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -138,7 +138,6 @@ BlockAIOCB *blk_aio_discard(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque); void blk_aio_cancel(BlockAIOCB *acb); void blk_aio_cancel_async(BlockAIOCB *acb); -int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs); int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 4a00bc604d..22f2ecf6e5 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -574,49 +574,6 @@ static int do_aio_writev(BlockBackend *blk, QEMUIOVector *qiov, return async_ret < 0 ? async_ret : 1; } -struct multiwrite_async_ret { - int num_done; - int error; -}; - -static void multiwrite_cb(void *opaque, int ret) -{ - struct multiwrite_async_ret *async_ret = opaque; - - async_ret->num_done++; - if (ret < 0) { - async_ret->error = ret; - } -} - -static int do_aio_multiwrite(BlockBackend *blk, BlockRequest* reqs, - int num_reqs, int *total) -{ - int i, ret; - struct multiwrite_async_ret async_ret = { - .num_done = 0, - .error = 0, - }; - - *total = 0; - for (i = 0; i < num_reqs; i++) { - reqs[i].cb = multiwrite_cb; - reqs[i].opaque = &async_ret; - *total += reqs[i].qiov->size; - } - - ret = blk_aio_multiwrite(blk, reqs, num_reqs); - if (ret < 0) { - return ret; - } - - while (async_ret.num_done < num_reqs) { - main_loop_wait(false); - } - - return async_ret.error < 0 ? async_ret.error : 1; -} - static void read_help(void) { printf( @@ -1211,165 +1168,6 @@ out: return 0; } -static void multiwrite_help(void) -{ - printf( -"\n" -" writes a range of bytes from the given offset source from multiple buffers,\n" -" in a batch of requests that may be merged by qemu\n" -"\n" -" Example:\n" -" 'multiwrite 512 1k 1k ; 4k 1k'\n" -" writes 2 kB at 512 bytes and 1 kB at 4 kB into the open file\n" -"\n" -" Writes into a segment of the currently open file, using a buffer\n" -" filled with a set pattern (0xcdcdcdcd). The pattern byte is increased\n" -" by one for each request contained in the multiwrite command.\n" -" -P, -- use different pattern to fill file\n" -" -C, -- report statistics in a machine parsable format\n" -" -q, -- quiet mode, do not show I/O statistics\n" -"\n"); -} - -static int multiwrite_f(BlockBackend *blk, int argc, char **argv); - -static const cmdinfo_t multiwrite_cmd = { - .name = "multiwrite", - .cfunc = multiwrite_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]", - .oneline = "issues multiple write requests at once", - .help = multiwrite_help, -}; - -static int multiwrite_f(BlockBackend *blk, int argc, char **argv) -{ - struct timeval t1, t2; - bool Cflag = false, qflag = false; - int c, cnt; - char **buf; - int64_t offset, first_offset = 0; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - int nr_reqs; - int pattern = 0xcd; - QEMUIOVector *qiovs; - int i; - BlockRequest *reqs; - - while ((c = getopt(argc, argv, "CqP:")) != -1) { - switch (c) { - case 'C': - Cflag = true; - break; - case 'q': - qflag = true; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) { - return 0; - } - break; - default: - return qemuio_command_usage(&writev_cmd); - } - } - - if (optind > argc - 2) { - return qemuio_command_usage(&writev_cmd); - } - - nr_reqs = 1; - for (i = optind; i < argc; i++) { - if (!strcmp(argv[i], ";")) { - nr_reqs++; - } - } - - reqs = g_new0(BlockRequest, nr_reqs); - buf = g_new0(char *, nr_reqs); - qiovs = g_new(QEMUIOVector, nr_reqs); - - for (i = 0; i < nr_reqs && optind < argc; i++) { - int j; - - /* Read the offset of the request */ - offset = cvtnum(argv[optind]); - if (offset < 0) { - print_cvtnum_err(offset, argv[optind]); - goto out; - } - optind++; - - if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); - goto out; - } - - if (i == 0) { - first_offset = offset; - } - - /* Read lengths for qiov entries */ - for (j = optind; j < argc; j++) { - if (!strcmp(argv[j], ";")) { - break; - } - } - - nr_iov = j - optind; - - /* Build request */ - buf[i] = create_iovec(blk, &qiovs[i], &argv[optind], nr_iov, pattern); - if (buf[i] == NULL) { - goto out; - } - - reqs[i].qiov = &qiovs[i]; - reqs[i].sector = offset >> 9; - reqs[i].nb_sectors = reqs[i].qiov->size >> 9; - - optind = j + 1; - - pattern++; - } - - /* If there were empty requests at the end, ignore them */ - nr_reqs = i; - - gettimeofday(&t1, NULL); - cnt = do_aio_multiwrite(blk, reqs, nr_reqs, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("aio_multiwrite failed: %s\n", strerror(-cnt)); - goto out; - } - - if (qflag) { - goto out; - } - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("wrote", &t2, first_offset, total, total, cnt, Cflag); -out: - for (i = 0; i < nr_reqs; i++) { - qemu_io_free(buf[i]); - if (reqs[i].qiov != NULL) { - qemu_iovec_destroy(&qiovs[i]); - } - } - g_free(buf); - g_free(reqs); - g_free(qiovs); - return 0; -} - struct aio_ctx { BlockBackend *blk; QEMUIOVector qiov; @@ -2436,7 +2234,6 @@ static void __attribute((constructor)) init_qemuio_commands(void) qemuio_add_command(&readv_cmd); qemuio_add_command(&write_cmd); qemuio_add_command(&writev_cmd); - qemuio_add_command(&multiwrite_cmd); qemuio_add_command(&aio_read_cmd); qemuio_add_command(&aio_write_cmd); qemuio_add_command(&aio_flush_cmd); diff --git a/tests/qemu-iotests/100 b/tests/qemu-iotests/100 deleted file mode 100755 index e66db07982..0000000000 --- a/tests/qemu-iotests/100 +++ /dev/null @@ -1,152 +0,0 @@ -#!/bin/bash -# -# Test simple read/write using plain bdrv_read/bdrv_write -# -# Copyright (C) 2014 Red Hat, Inc. -# -# 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 . -# - -# creator -owner=stefanha@redhat.com - -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 generic -_supported_proto generic -_supported_os Linux - - -size=128M - -echo -echo "== Single request ==" -_make_test_img $size -$QEMU_IO -c "write -z 0 8k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "multiwrite 0 4k" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verify pattern ==" -$QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io - -_cleanup_test_img - -echo -echo "== Sequential requests ==" -_make_test_img $size -$QEMU_IO -c "write -z 0 12k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "multiwrite 0 4k ; 4k 4k" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verify pattern ==" -$QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0xce 4k 4k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 8k 4k" "$TEST_IMG" | _filter_qemu_io - -_cleanup_test_img - -echo -echo "== Superset overlapping requests ==" -_make_test_img $size -$QEMU_IO -c "write -z 0 8k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "multiwrite 0 4k ; 1k 2k" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verify pattern ==" -# Order of overlapping in-flight requests is not guaranteed so we cannot verify -# [1k, 3k) since it could have either pattern 0xcd or 0xce. -$QEMU_IO -c "read -P 0xcd 0 1k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0xcd 3k 1k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io - -_cleanup_test_img - -echo -echo "== Subset overlapping requests ==" -_make_test_img $size -$QEMU_IO -c "write -z 0 8k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "multiwrite 1k 2k ; 0k 4k" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verify pattern ==" -# Order of overlapping in-flight requests is not guaranteed so we cannot verify -# [1k, 3k) since it could have either pattern 0xcd or 0xce. -$QEMU_IO -c "read -P 0xce 0 1k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0xce 3k 1k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io - -_cleanup_test_img - -echo -echo "== Head overlapping requests ==" -_make_test_img $size -$QEMU_IO -c "write -z 0 8k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "multiwrite 0k 2k ; 0k 4k" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verify pattern ==" -# Order of overlapping in-flight requests is not guaranteed so we cannot verify -# [0k, 2k) since it could have either pattern 0xcd or 0xce. -$QEMU_IO -c "read -P 0xce 2k 2k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io - -_cleanup_test_img - -echo -echo "== Tail overlapping requests ==" -_make_test_img $size -$QEMU_IO -c "write -z 0 8k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "multiwrite 2k 2k ; 0k 4k" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verify pattern ==" -# Order of overlapping in-flight requests is not guaranteed so we cannot verify -# [2k, 4k) since it could have either pattern 0xcd or 0xce. -$QEMU_IO -c "read -P 0xce 0k 2k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io - -_cleanup_test_img - -echo -echo "== Disjoint requests ==" -_make_test_img $size -$QEMU_IO -c "write -z 0 72k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "multiwrite 0 4k ; 64k 4k" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verify pattern ==" -$QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 4k 60k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0xce 64k 4k" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0 68k 4k" "$TEST_IMG" | _filter_qemu_io - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 diff --git a/tests/qemu-iotests/100.out b/tests/qemu-iotests/100.out deleted file mode 100644 index a44cae40db..0000000000 --- a/tests/qemu-iotests/100.out +++ /dev/null @@ -1,103 +0,0 @@ -QA output created by 100 - -== Single request == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -wrote 8192/8192 bytes at offset 0 -8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 4096/4096 bytes at offset 0 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== verify pattern == -read 4096/4096 bytes at offset 0 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 4096 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== Sequential requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -wrote 12288/12288 bytes at offset 0 -12 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 8192/8192 bytes at offset 0 -8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== verify pattern == -read 4096/4096 bytes at offset 0 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 4096 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 8192 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== Superset overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -wrote 8192/8192 bytes at offset 0 -8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 6144/6144 bytes at offset 0 -6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== verify pattern == -read 1024/1024 bytes at offset 0 -1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 1024/1024 bytes at offset 3072 -1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 4096 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== Subset overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -wrote 8192/8192 bytes at offset 0 -8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 6144/6144 bytes at offset 1024 -6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== verify pattern == -read 1024/1024 bytes at offset 0 -1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 1024/1024 bytes at offset 3072 -1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 4096 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== Head overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -wrote 8192/8192 bytes at offset 0 -8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 6144/6144 bytes at offset 0 -6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== verify pattern == -read 2048/2048 bytes at offset 2048 -2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 4096 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== Tail overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -wrote 8192/8192 bytes at offset 0 -8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 6144/6144 bytes at offset 2048 -6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== verify pattern == -read 2048/2048 bytes at offset 0 -2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 4096 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== Disjoint requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -wrote 73728/73728 bytes at offset 0 -72 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 8192/8192 bytes at offset 0 -8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== verify pattern == -read 4096/4096 bytes at offset 0 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 61440/61440 bytes at offset 4096 -60 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 65536 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4096/4096 bytes at offset 69632 -4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -*** done diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index e8c6937fc9..996265e626 100644 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 @@ -248,14 +248,6 @@ sector = "%d" if failed_wr_ops > 0: highest_offset = max(highest_offset, bad_offset + 512) - for i in range(wr_merged): - first = i * wr_size * 2 - second = first + wr_size - ops.append("multiwrite %d %d ; %d %d" % - (first, wr_size, second, wr_size)) - - highest_offset = max(highest_offset, wr_merged * wr_size * 2) - # Now perform all operations for op in ops: self.vm.hmp_qemu_io("drive0", op) @@ -309,19 +301,15 @@ sector = "%d" def test_flush(self): self.do_test_stats(flush_ops = 8) - def test_merged(self): - for i in range(5): - self.do_test_stats(wr_merged = i * 3) - def test_all(self): # rd_size, rd_ops, wr_size, wr_ops, flush_ops # invalid_rd_ops, invalid_wr_ops, # failed_rd_ops, failed_wr_ops # wr_merged - test_values = [[512, 1, 512, 1, 1, 4, 7, 5, 2, 1], - [65536, 1, 2048, 12, 7, 7, 5, 2, 5, 5], - [32768, 9, 8192, 1, 4, 3, 2, 4, 6, 4], - [16384, 11, 3584, 16, 9, 8, 6, 7, 3, 4]] + test_values = [[512, 1, 512, 1, 1, 4, 7, 5, 2, 0], + [65536, 1, 2048, 12, 7, 7, 5, 2, 5, 0], + [32768, 9, 8192, 1, 4, 3, 2, 4, 6, 0], + [16384, 11, 3584, 16, 9, 8, 6, 7, 3, 0]] for i in test_values: self.do_test_stats(*i) diff --git a/tests/qemu-iotests/136.out b/tests/qemu-iotests/136.out index 0a5e9583a4..cfa5c0d0e6 100644 --- a/tests/qemu-iotests/136.out +++ b/tests/qemu-iotests/136.out @@ -1,5 +1,5 @@ -........................................ +................................... ---------------------------------------------------------------------- -Ran 40 tests +Ran 35 tests OK diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 822953b6fa..60676738d0 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -106,7 +106,7 @@ 097 rw auto backing 098 rw auto backing quick 099 rw auto quick -100 rw auto quick +# 100 was removed, do not reuse 101 rw auto quick 102 rw auto quick 103 rw auto quick diff --git a/trace-events b/trace-events index e35b80e980..b53c3541a3 100644 --- a/trace-events +++ b/trace-events @@ -62,8 +62,6 @@ bdrv_open_common(void *bs, const char *filename, int flags, const char *format_n bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" # block/io.c -multiwrite_cb(void *mcb, int ret) "mcb %p ret %d" -bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d" bdrv_aio_discard(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p" bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" -- cgit 1.4.1 From 79c719b755134da3dd2ba2a63a9a7db765f68e53 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 22 Mar 2016 20:20:29 +0100 Subject: block: Don't return throttling info in query-named-block-nodes query-named-block-nodes should not return information that is related to the attached BlockBackend rather than the node itself, so throttling information needs to be removed from it. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/qapi.c | 6 +++--- tests/qemu-iotests/096 | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/block/qapi.c b/block/qapi.c index b0d8d6b71e..5594f74d17 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -67,10 +67,10 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->backing_file_depth = bdrv_get_backing_file_depth(bs); info->detect_zeroes = bs->detect_zeroes; - if (bs->blk && blk_get_public(bs->blk)->throttle_state) { + if (blk && blk_get_public(blk)->throttle_state) { ThrottleConfig cfg; - throttle_group_get_config(bs->blk, &cfg); + throttle_group_get_config(blk, &cfg); info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; @@ -118,7 +118,7 @@ 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(bs->blk)); + info->group = g_strdup(throttle_group_get_name(blk)); } info->write_threshold = bdrv_write_threshold_get(bs); diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096 index e34204b8ff..aeeb3753cf 100644 --- a/tests/qemu-iotests/096 +++ b/tests/qemu-iotests/096 @@ -45,8 +45,9 @@ class TestLiveSnapshot(iotests.QMPTestCase): os.remove(self.target_img) def checkConfig(self, active_layer): - result = self.vm.qmp('query-named-block-nodes') + result = self.vm.qmp('query-block') for r in result['return']: + r = r['inserted'] if r['node-name'] == active_layer: self.assertEqual(r['group'], self.group) self.assertEqual(r['iops'], self.iops) -- cgit 1.4.1 From 1ef7d010216b7d1046a3f6e31b49093addad01ce Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 17 May 2016 18:37:39 +0200 Subject: qemu-iotests: Some more write_zeroes tests This covers some more write_zeroes cases which are relevant for the recent qcow2 optimisations that check the allocation status of the backing file for partial cluster write_zeroes requests. This needs to be separate from 034 because we can only support qcow2 in this test case for multiple reasons: We check the allocation status after write_zeroes with 'qemu-img map' and the optimised behaviour that produces zero clusters is only implemented in qcow2; second, the map command returns offsets that are qcow2 specific; and finally, we also use 512 byte clusters which aren't supported for formats like qed. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake --- tests/qemu-iotests/154 | 265 +++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/154.out | 242 +++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 508 insertions(+) create mode 100755 tests/qemu-iotests/154 create mode 100644 tests/qemu-iotests/154.out (limited to 'tests') diff --git a/tests/qemu-iotests/154 b/tests/qemu-iotests/154 new file mode 100755 index 0000000000..23f1b3ab16 --- /dev/null +++ b/tests/qemu-iotests/154 @@ -0,0 +1,265 @@ +#!/bin/bash +# +# qcow2 specific bdrv_write_zeroes tests with backing files (complements 034) +# +# Copyright (C) 2016 Red Hat, Inc. +# +# 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 . +# + +# creator +owner=kwolf@redhat.com + +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 + +CLUSTER_SIZE=4k +size=128M + +echo +echo == backing file contains zeros == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Make sure that the whole cluster is allocated even for partial write_zeroes +# when the backing file contains zeros + +# X = non-zero data sector in backing file +# - = sector unallocated in whole backing chain +# 0 = sector touched by write_zeroes request + +# 1. Tail unaligned: 00 00 -- -- +# 2. Head unaligned: -- -- 00 00 +# 3. Both unaligned: -- 00 00 -- +# 4. Both, 2 clusters: -- -- -- 00 | 00 -- -- -- + +$QEMU_IO -c "write -z 0 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 10k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 17k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 27k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == backing file contains non-zero data before write_zeroes == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Single cluster; non-zero data at the cluster start +# ... | XX -- 00 -- | ... +$QEMU_IO -c "write -P 0x11 32k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 34k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 32k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 33k 3k" "$TEST_IMG" | _filter_qemu_io + +# Single cluster; non-zero data exists, but not at the cluster start +# ... | -- XX 00 -- | ... +$QEMU_IO -c "write -P 0x11 65k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 66k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 65k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 64k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 66k 2k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == backing file contains non-zero data after write_zeroes == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Single cluster; non-zero data directly after request +# ... | -- 00 XX -- | ... +$QEMU_IO -c "write -P 0x11 34k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 33k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 32k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 34k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 35k 1k" "$TEST_IMG" | _filter_qemu_io + +# Single cluster; non-zero data exists, but not directly after request +# ... | -- 00 -- XX | ... +$QEMU_IO -c "write -P 0x11 43k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 41k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 43k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 40k 3k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == spanning two clusters, non-zero before request == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Two clusters; non-zero data before request: +# 1. At cluster start: 32k: XX -- -- 00 | 00 -- -- -- +# 2. Between unallocated space: 48k: -- XX -- 00 | 00 -- -- -- +# 3. Directly before request: 64k: -- -- XX 00 | 00 -- -- -- + +$QEMU_IO -c "write -P 0x11 32k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 35k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 32k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 33k 7k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "write -P 0x11 49k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 51k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 48k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 49k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 50k 6k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "write -P 0x11 66k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 67k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 64k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 66k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 67k 5k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == spanning two clusters, non-zero after request == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Two clusters; non-zero data after request: +# 1. Directly after request: 32k: -- -- -- 00 | 00 XX -- -- +# 2. Between unallocated space: 48k: -- -- -- 00 | 00 -- XX -- +# 3. At cluster end: 64k: -- -- -- 00 | 00 -- -- XX + +$QEMU_IO -c "write -P 0x11 37k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 35k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 32k 5k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 37k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 38k 2k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "write -P 0x11 54k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 51k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 48k 6k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 54k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 55k 1k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "write -P 0x11 71k 1k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 67k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 64k 7k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 71k 1k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == spanning two clusters, partially overwriting backing file == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Backing file: -- -- XX XX | XX XX -- -- +# Active layer: -- -- XX 00 | 00 XX -- -- + +$QEMU_IO -c "write -P 0x11 2k 4k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 3k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 0k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 2k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 3k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 5k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 6k 2k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == spanning multiple clusters, non-zero in first cluster == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Backing file: 64k: XX XX -- -- | -- -- -- -- | -- -- -- -- +# Active layer: 64k: XX XX 00 00 | 00 00 00 00 | 00 -- -- -- + +$QEMU_IO -c "write -P 0x11 64k 2k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 66k 7k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 64k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 66k 10k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == spanning multiple clusters, non-zero in intermediate cluster == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Backing file: 64k: -- -- -- -- | -- XX XX -- | -- -- -- -- +# Active layer: 64k: -- -- 00 00 | 00 00 00 00 | 00 -- -- -- + +$QEMU_IO -c "write -P 0x11 69k 2k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 66k 7k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 64k 12k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == spanning multiple clusters, non-zero in final cluster == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Backing file: 64k: -- -- -- -- | -- -- -- -- | -- -- XX XX +# Active layer: 64k: -- -- 00 00 | 00 00 00 00 | 00 -- XX XX + +$QEMU_IO -c "write -P 0x11 74k 2k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 66k 7k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 64k 10k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 74k 2k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo +echo == spanning multiple clusters, partially overwriting backing file == + +CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" + +# Backing file: 64k: -- XX XX XX | XX XX XX XX | XX XX XX -- +# Active layer: 64k: -- XX 00 00 | 00 00 00 00 | 00 XX XX -- + +$QEMU_IO -c "write -P 0x11 65k 10k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 66k 7k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 64k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 65k 1k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 66k 7k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0x11 73k 2k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0 75k 1k" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/154.out b/tests/qemu-iotests/154.out new file mode 100644 index 0000000000..8946b734ac --- /dev/null +++ b/tests/qemu-iotests/154.out @@ -0,0 +1,242 @@ +QA output created by 154 + +== backing file contains zeros == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 2048/2048 bytes at offset 0 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 10240 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 17408 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 27648 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 4096, "length": 4096, "depth": 1, "zero": true, "data": false}, +{ "start": 8192, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 12288, "length": 4096, "depth": 1, "zero": true, "data": false}, +{ "start": 16384, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 20480, "length": 4096, "depth": 1, "zero": true, "data": false}, +{ "start": 24576, "length": 8192, "depth": 0, "zero": true, "data": false}, +{ "start": 32768, "length": 134184960, "depth": 1, "zero": true, "data": false}] + +== backing file contains non-zero data before write_zeroes == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 1024/1024 bytes at offset 32768 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 34816 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 32768 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 3072/3072 bytes at offset 33792 +3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 66560 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 67584 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 66560 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 65536 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 67584 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false}, +{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 36864, "length": 28672, "depth": 1, "zero": true, "data": false}, +{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576}, +{ "start": 69632, "length": 134148096, "depth": 1, "zero": true, "data": false}] + +== backing file contains non-zero data after write_zeroes == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 1024/1024 bytes at offset 34816 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 33792 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 32768 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 34816 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 35840 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 44032 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 41984 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 44032 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 3072/3072 bytes at offset 40960 +3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false}, +{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 36864, "length": 4096, "depth": 1, "zero": true, "data": false}, +{ "start": 40960, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576}, +{ "start": 45056, "length": 134172672, "depth": 1, "zero": true, "data": false}] + +== spanning two clusters, non-zero before request == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 1024/1024 bytes at offset 32768 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 35840 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 32768 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 7168/7168 bytes at offset 33792 +7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 50176 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 52224 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 49152 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 50176 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 6144/6144 bytes at offset 51200 +6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 67584 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 68608 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 65536 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 67584 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 5120/5120 bytes at offset 68608 +5 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false}, +{ "start": 32768, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false}, +{ "start": 49152, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 28672}, +{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false}, +{ "start": 65536, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 36864}, +{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}] + +== spanning two clusters, non-zero after request == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 1024/1024 bytes at offset 37888 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 35840 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 5120/5120 bytes at offset 32768 +5 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 37888 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 38912 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 55296 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 52224 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 6144/6144 bytes at offset 49152 +6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 55296 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 56320 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1024/1024 bytes at offset 72704 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 68608 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 7168/7168 bytes at offset 65536 +7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 72704 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false}, +{ "start": 32768, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false}, +{ "start": 49152, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 28672}, +{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false}, +{ "start": 65536, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 36864}, +{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}] + +== spanning two clusters, partially overwriting backing file == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 4096/4096 bytes at offset 2048 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 3072 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 0 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 2048 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 3072 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 5120 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 6144 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 8192, "length": 134209536, "depth": 1, "zero": true, "data": false}] + +== spanning multiple clusters, non-zero in first cluster == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 2048/2048 bytes at offset 65536 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 7168/7168 bytes at offset 67584 +7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 65536 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 10240/10240 bytes at offset 67584 +10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false}, +{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 69632, "length": 8192, "depth": 0, "zero": true, "data": false}, +{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}] + +== spanning multiple clusters, non-zero in intermediate cluster == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 2048/2048 bytes at offset 70656 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 7168/7168 bytes at offset 67584 +7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 12288/12288 bytes at offset 65536 +12 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false}, +{ "start": 65536, "length": 12288, "depth": 0, "zero": true, "data": false}, +{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}] + +== spanning multiple clusters, non-zero in final cluster == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 2048/2048 bytes at offset 75776 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 7168/7168 bytes at offset 67584 +7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 10240/10240 bytes at offset 65536 +10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 75776 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false}, +{ "start": 65536, "length": 8192, "depth": 0, "zero": true, "data": false}, +{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}] + +== spanning multiple clusters, partially overwriting backing file == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base +wrote 10240/10240 bytes at offset 66560 +10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 7168/7168 bytes at offset 67584 +7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 65536 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 66560 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 7168/7168 bytes at offset 67584 +7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 74752 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 76800 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false}, +{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480}, +{ "start": 69632, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576}, +{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}] +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 60676738d0..ab1d76efdf 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -153,3 +153,4 @@ 149 rw auto sudo 150 rw auto quick 152 rw auto quick +154 rw auto backing quick -- cgit 1.4.1 From 9e28bb26c243c2c0ec96a900611f0658a0665b43 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 16 May 2016 10:43:02 -0600 Subject: qemu-iotests: Simplify 109 with unaligned qemu-img compare For some time now, qemu-img compare has been able to compare unaligned images. So we no longer need test 109's hack of resizing to sector boundaries before invoking compare. Signed-off-by: Eric Blake Reviewed-by: Max Reitz Message-id: 1463416983-28318-3-git-send-email-eblake@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/109 | 2 -- tests/qemu-iotests/109.out | 4 ---- 2 files changed, 6 deletions(-) (limited to 'tests') diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index f980b0c9e5..adf98892f0 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -104,8 +104,6 @@ for sample_img in empty.bochs iotest-dirtylog-10G-4M.vhdx parallels-v1 \ $QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io run_qemu "$TEST_IMG" "$TEST_IMG.src" "'format': 'raw'," "BLOCK_JOB_READY" - # qemu-img compare can't handle unaligned file sizes - $QEMU_IMG resize -f raw "$TEST_IMG.src" +0 $QEMU_IMG compare -f raw -F raw "$TEST_IMG" "$TEST_IMG.src" done diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 38bc073a37..7c797ed31c 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -143,7 +143,6 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} -Image resized. Warning: Image size mismatch! Images are identical. @@ -164,7 +163,6 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 31457280, "offset": 31457280, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} -Image resized. Warning: Image size mismatch! Images are identical. @@ -185,7 +183,6 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} -Image resized. Warning: Image size mismatch! Images are identical. @@ -206,7 +203,6 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2048, "offset": 2048, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} -Image resized. Warning: Image size mismatch! Images are identical. -- cgit 1.4.1 From 37546ff28fb89744ebf2223db22cbc253592abe1 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 16 May 2016 10:43:03 -0600 Subject: qemu-iotests: Fix regression in 136 on aio_read invalid Commit 093ea232 removed the ability for aio_read and aio_write to artificially inflate the invalid statistics counters for block devices, since it no longer flags unaligned offset or length. Add 'aio_read -i' and 'aio_write -i' to restore the ability, and update test 136 to use it. Reported-by: Kevin Wolf Signed-off-by: Eric Blake Message-id: 1463416983-28318-4-git-send-email-eblake@redhat.com Signed-off-by: Max Reitz --- qemu-io-cmds.c | 20 ++++++++++++++++---- tests/qemu-iotests/136 | 15 ++++----------- 2 files changed, 20 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index b5dbc676aa..e766791ffc 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1274,6 +1274,7 @@ static void aio_read_help(void) " used to ensure all outstanding aio requests have been completed.\n" " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" +" -i, -- treat request as invalid, for exercising stats\n" " -v, -- dump buffer to standard output\n" " -q, -- quiet mode, do not show I/O statistics\n" "\n"); @@ -1286,7 +1287,7 @@ static const cmdinfo_t aio_read_cmd = { .cfunc = aio_read_f, .argmin = 2, .argmax = -1, - .args = "[-Cqv] [-P pattern] off len [len..]", + .args = "[-Ciqv] [-P pattern] off len [len..]", .oneline = "asynchronously reads a number of bytes", .help = aio_read_help, }; @@ -1297,7 +1298,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); ctx->blk = blk; - while ((c = getopt(argc, argv, "CP:qv")) != -1) { + while ((c = getopt(argc, argv, "CP:iqv")) != -1) { switch (c) { case 'C': ctx->Cflag = true; @@ -1310,6 +1311,11 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) return 0; } break; + case 'i': + printf("injecting invalid read request\n"); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); + g_free(ctx); + return 0; case 'q': ctx->qflag = true; break; @@ -1367,6 +1373,7 @@ static void aio_write_help(void) " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" " -f, -- use Force Unit Access semantics\n" +" -i, -- treat request as invalid, for exercising stats\n" " -q, -- quiet mode, do not show I/O statistics\n" " -u, -- with -z, allow unmapping\n" " -z, -- write zeroes using blk_aio_write_zeroes\n" @@ -1380,7 +1387,7 @@ static const cmdinfo_t aio_write_cmd = { .cfunc = aio_write_f, .argmin = 2, .argmax = -1, - .args = "[-Cfquz] [-P pattern] off len [len..]", + .args = "[-Cfiquz] [-P pattern] off len [len..]", .oneline = "asynchronously writes a number of bytes", .help = aio_write_help, }; @@ -1393,7 +1400,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) int flags = 0; ctx->blk = blk; - while ((c = getopt(argc, argv, "CfqP:uz")) != -1) { + while ((c = getopt(argc, argv, "CfiqP:uz")) != -1) { switch (c) { case 'C': ctx->Cflag = true; @@ -1414,6 +1421,11 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) return 0; } break; + case 'i': + printf("injecting invalid write request\n"); + block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); + g_free(ctx); + return 0; case 'z': ctx->zflag = true; break; diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index 996265e626..635b977552 100644 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 @@ -226,18 +226,11 @@ sector = "%d" highest_offset = wr_ops * wr_size - # Two types of invalid operations: unaligned length and unaligned offset - for i in range(invalid_rd_ops / 2): - ops.append("aio_read 0 511") + for i in range(invalid_rd_ops): + ops.append("aio_read -i 0 512") - for i in range(invalid_rd_ops / 2, invalid_rd_ops): - ops.append("aio_read 13 512") - - for i in range(invalid_wr_ops / 2): - ops.append("aio_write 0 511") - - for i in range(invalid_wr_ops / 2, invalid_wr_ops): - ops.append("aio_write 13 512") + for i in range(invalid_wr_ops): + ops.append("aio_write -i 0 512") for i in range(failed_rd_ops): ops.append("aio_read %d 512" % bad_offset) -- cgit 1.4.1