From 5fbfabd313b77e1cc7038ae8c4481c4b9f8b650a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 30 Nov 2017 17:38:43 +0100 Subject: [PATCH 01/35] block: Formats don't need CONSISTENT_READ with NO_IO Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently in use as a mirror target. It is not enough for image formats, though, as these still unconditionally request BLK_PERM_CONSISTENT_READ. As this permission is geared towards whether the guest-visible data is consistent, and has no impact on whether the metadata is sane, and 'qemu-img info' does not read guest-visible data (except for the raw format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there is not going to be any guest I/O performed, regardless of image format. Signed-off-by: Kevin Wolf --- block.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/block.c b/block.c index 1c37ce4554..db0be7ef36 100644 --- a/block.c +++ b/block.c @@ -1924,6 +1924,8 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, assert(role == &child_backing || role == &child_file); if (!backing) { + int flags = bdrv_reopen_get_flags(reopen_queue, bs); + /* Apart from the modifications below, the same permissions are * forwarded and left alone as for filters */ bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, @@ -1936,7 +1938,9 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, /* bs->file always needs to be consistent because of the metadata. We * can never allow other users to resize or write to it. */ - perm |= BLK_PERM_CONSISTENT_READ; + if (!(flags & BDRV_O_NO_IO)) { + perm |= BLK_PERM_CONSISTENT_READ; + } shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); } else { /* We want consistent read from backing files if the parent needs it. From bff5554843773d8ca644fddd37ffebcc952ce48f Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 4 Dec 2017 20:08:20 -0500 Subject: [PATCH 02/35] iotests: fix 197 for vpc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VPC has some difficulty creating geometries of particular size. However, we can indeed force it to use a literal one, so let's do that for the sake of test 197, which is testing some specific offsets. Signed-off-by: John Snow Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf Reviewed-by: Lukáš Doktor --- tests/qemu-iotests/197 | 4 ++++ tests/qemu-iotests/common.filter | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 index 887eb4f496..5e869fe2b7 100755 --- a/tests/qemu-iotests/197 +++ b/tests/qemu-iotests/197 @@ -60,6 +60,10 @@ echo '=== Copy-on-read ===' echo # Prep the images +# VPC rounds image sizes to a specific geometry, force a specific size. +if [ "$IMGFMT" = "vpc" ]; then + IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size") +fi _make_test_img 4G $QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index d9237799e9..f08248bfd9 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -134,7 +134,8 @@ _filter_img_create() -e "s# log_size=[0-9]\\+##g" \ -e "s# refcount_bits=[0-9]\\+##g" \ -e "s# key-secret=[a-zA-Z0-9]\\+##g" \ - -e "s# iter-time=[0-9]\\+##g" + -e "s# iter-time=[0-9]\\+##g" \ + -e "s# force_size=\\(on\\|off\\)##g" } _filter_img_info() From db0289b9b26cb653d5662f5d6a2a52d70243cd56 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 5 Dec 2017 12:52:09 +0100 Subject: [PATCH 03/35] block: Make bdrv_drain_invoke() recursive This change separates bdrv_drain_invoke(), which calls the BlockDriver drain callbacks, from bdrv_drain_recurse(). Instead, the function performs its own recursion now. One reason for this is that bdrv_drain_recurse() can be called multiple times by bdrv_drain_all_begin(), but the callbacks may only be called once. The separation is necessary to fix this bug. The other reason is that we intend to go to a model where we call all driver callbacks first, and only then start polling. This is not fully achieved yet with this patch, as bdrv_drain_invoke() contains a BDRV_POLL_WHILE() loop for the block driver callbacks, which can still call callbacks for any unrelated event. It's a step in this direction anyway. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/block/io.c b/block/io.c index 6773926fc1..096468b761 100644 --- a/block/io.c +++ b/block/io.c @@ -175,8 +175,10 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) bdrv_wakeup(bs); } +/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) { + BdrvChild *child, *tmp; BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || @@ -187,6 +189,10 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); bdrv_coroutine_enter(bs, data.co); BDRV_POLL_WHILE(bs, !data.done); + + QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { + bdrv_drain_invoke(child->bs, begin); + } } static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) @@ -194,9 +200,6 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) BdrvChild *child, *tmp; bool waited; - /* Ensure any pending metadata writes are submitted to bs->file. */ - bdrv_drain_invoke(bs, begin); - /* Wait for drained requests to finish */ waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); @@ -279,6 +282,7 @@ void bdrv_drained_begin(BlockDriverState *bs) bdrv_parent_drained_begin(bs); } + bdrv_drain_invoke(bs, true); bdrv_drain_recurse(bs, true); } @@ -294,6 +298,7 @@ void bdrv_drained_end(BlockDriverState *bs) } bdrv_parent_drained_end(bs); + bdrv_drain_invoke(bs, false); bdrv_drain_recurse(bs, false); aio_enable_external(bdrv_get_aio_context(bs)); } @@ -372,6 +377,8 @@ void bdrv_drain_all_begin(void) aio_context_acquire(aio_context); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { if (aio_context == bdrv_get_aio_context(bs)) { + /* FIXME Calling this multiple times is wrong */ + bdrv_drain_invoke(bs, true); waited |= bdrv_drain_recurse(bs, true); } } @@ -393,6 +400,7 @@ void bdrv_drain_all_end(void) aio_context_acquire(aio_context); aio_enable_external(aio_context); bdrv_parent_drained_end(bs); + bdrv_drain_invoke(bs, false); bdrv_drain_recurse(bs, false); aio_context_release(aio_context); } From 2da9b7d456278bccc6ce889ae350f2867155d7e8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 5 Dec 2017 13:53:35 +0100 Subject: [PATCH 04/35] block: Call .drain_begin only once in bdrv_drain_all_begin() bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver callback inside its polling loop. This means that how many times it got called for each node depended on long it had to poll the event loop. This is obviously not right and results in nodes that stay drained even after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per node. Fix bdrv_drain_all_begin() to call the callback only once, too. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/block/io.c b/block/io.c index 096468b761..603f5b059e 100644 --- a/block/io.c +++ b/block/io.c @@ -355,6 +355,7 @@ void bdrv_drain_all_begin(void) aio_context_acquire(aio_context); bdrv_parent_drained_begin(bs); aio_disable_external(aio_context); + bdrv_drain_invoke(bs, true); aio_context_release(aio_context); if (!g_slist_find(aio_ctxs, aio_context)) { @@ -377,8 +378,6 @@ void bdrv_drain_all_begin(void) aio_context_acquire(aio_context); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { if (aio_context == bdrv_get_aio_context(bs)) { - /* FIXME Calling this multiple times is wrong */ - bdrv_drain_invoke(bs, true); waited |= bdrv_drain_recurse(bs, true); } } From 881cfd17c734634de0f4bc6725940cca4fd314c9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 5 Dec 2017 14:05:02 +0100 Subject: [PATCH 05/35] test-bdrv-drain: Test BlockDriver callbacks for drain This adds a test case that the BlockDriver callbacks for drain are called in bdrv_drained_all_begin/end(), and that both of them are called exactly once. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi Reviewed-by: Eric Blake --- tests/Makefile.include | 2 + tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 tests/test-bdrv-drain.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 77f8183117..39a4b5359d 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -80,6 +80,7 @@ gcov-files-test-thread-pool-y = thread-pool.c gcov-files-test-hbitmap-y = util/hbitmap.c check-unit-y += tests/test-hbitmap$(EXESUF) gcov-files-test-hbitmap-y = blockjob.c +check-unit-y += tests/test-bdrv-drain$(EXESUF) check-unit-y += tests/test-blockjob$(EXESUF) check-unit-y += tests/test-blockjob-txn$(EXESUF) check-unit-y += tests/test-x86-cpuid$(EXESUF) @@ -595,6 +596,7 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y) tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) +tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y) tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c new file mode 100644 index 0000000000..0e567e34ba --- /dev/null +++ b/tests/test-bdrv-drain.c @@ -0,0 +1,137 @@ +/* + * Block node draining tests + * + * Copyright (c) 2017 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" + +typedef struct BDRVTestState { + int drain_count; +} BDRVTestState; + +static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) +{ + BDRVTestState *s = bs->opaque; + s->drain_count++; +} + +static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) +{ + BDRVTestState *s = bs->opaque; + s->drain_count--; +} + +static void bdrv_test_close(BlockDriverState *bs) +{ + BDRVTestState *s = bs->opaque; + g_assert_cmpint(s->drain_count, >, 0); +} + +static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + /* We want this request to stay until the polling loop in drain waits for + * it to complete. We need to sleep a while as bdrv_drain_invoke() comes + * first and polls its result, too, but it shouldn't accidentally complete + * this request yet. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + + return 0; +} + +static BlockDriver bdrv_test = { + .format_name = "test", + .instance_size = sizeof(BDRVTestState), + + .bdrv_close = bdrv_test_close, + .bdrv_co_preadv = bdrv_test_co_preadv, + + .bdrv_co_drain_begin = bdrv_test_co_drain_begin, + .bdrv_co_drain_end = bdrv_test_co_drain_end, +}; + +static void aio_ret_cb(void *opaque, int ret) +{ + int *aio_ret = opaque; + *aio_ret = ret; +} + +static void test_drv_cb_drain_all(void) +{ + BlockBackend *blk; + BlockDriverState *bs; + BDRVTestState *s; + BlockAIOCB *acb; + int aio_ret; + + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = NULL, + .iov_len = 0, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + s = bs->opaque; + blk_insert_bs(blk, bs, &error_abort); + + /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ + g_assert_cmpint(s->drain_count, ==, 0); + bdrv_drain_all_begin(); + g_assert_cmpint(s->drain_count, ==, 1); + bdrv_drain_all_end(); + g_assert_cmpint(s->drain_count, ==, 0); + + /* Now do the same while a request is pending */ + aio_ret = -EINPROGRESS; + acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); + g_assert(acb != NULL); + g_assert_cmpint(aio_ret, ==, -EINPROGRESS); + + g_assert_cmpint(s->drain_count, ==, 0); + bdrv_drain_all_begin(); + g_assert_cmpint(aio_ret, ==, 0); + g_assert_cmpint(s->drain_count, ==, 1); + bdrv_drain_all_end(); + g_assert_cmpint(s->drain_count, ==, 0); + + bdrv_unref(bs); + blk_unref(blk); +} + +int main(int argc, char **argv) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); + + return g_test_run(); +} From 99c05de9180ae100fcabd5ed02d32b392dc1528c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 5 Dec 2017 14:10:15 +0100 Subject: [PATCH 06/35] block: bdrv_drain_recurse(): Remove unused begin parameter Now that the bdrv_drain_invoke() calls are pulled up to the callers of bdrv_drain_recurse(), the 'begin' parameter isn't needed any more. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/block/io.c b/block/io.c index 603f5b059e..390d463c71 100644 --- a/block/io.c +++ b/block/io.c @@ -195,7 +195,7 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) } } -static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) +static bool bdrv_drain_recurse(BlockDriverState *bs) { BdrvChild *child, *tmp; bool waited; @@ -218,7 +218,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) */ bdrv_ref(bs); } - waited |= bdrv_drain_recurse(bs, begin); + waited |= bdrv_drain_recurse(bs); if (in_main_loop) { bdrv_unref(bs); } @@ -283,7 +283,7 @@ void bdrv_drained_begin(BlockDriverState *bs) } bdrv_drain_invoke(bs, true); - bdrv_drain_recurse(bs, true); + bdrv_drain_recurse(bs); } void bdrv_drained_end(BlockDriverState *bs) @@ -299,7 +299,7 @@ void bdrv_drained_end(BlockDriverState *bs) bdrv_parent_drained_end(bs); bdrv_drain_invoke(bs, false); - bdrv_drain_recurse(bs, false); + bdrv_drain_recurse(bs); aio_enable_external(bdrv_get_aio_context(bs)); } @@ -378,7 +378,7 @@ void bdrv_drain_all_begin(void) aio_context_acquire(aio_context); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { if (aio_context == bdrv_get_aio_context(bs)) { - waited |= bdrv_drain_recurse(bs, true); + waited |= bdrv_drain_recurse(bs); } } aio_context_release(aio_context); @@ -400,7 +400,7 @@ void bdrv_drain_all_end(void) aio_enable_external(aio_context); bdrv_parent_drained_end(bs); bdrv_drain_invoke(bs, false); - bdrv_drain_recurse(bs, false); + bdrv_drain_recurse(bs); aio_context_release(aio_context); } From 5280aa32e140a262bbc6e8e06fd4abb137900016 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Dec 2017 10:45:27 +0100 Subject: [PATCH 07/35] block: Don't wait for requests in bdrv_drain*_end() The device is drained, so there is no point in waiting for requests at the end of the drained section. Remove the bdrv_drain_recurse() calls there. The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e in order to call the .bdrv_co_drain_end() driver callback. This is now done by a separate bdrv_drain_invoke() call. Signed-off-by: Kevin Wolf Reviewed-by: Paolo Bonzini Reviewed-by: Stefan Hajnoczi --- block/io.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/block/io.c b/block/io.c index 390d463c71..5fdb92a15e 100644 --- a/block/io.c +++ b/block/io.c @@ -299,7 +299,6 @@ void bdrv_drained_end(BlockDriverState *bs) bdrv_parent_drained_end(bs); bdrv_drain_invoke(bs, false); - bdrv_drain_recurse(bs); aio_enable_external(bdrv_get_aio_context(bs)); } @@ -400,7 +399,6 @@ void bdrv_drain_all_end(void) aio_enable_external(aio_context); bdrv_parent_drained_end(bs); bdrv_drain_invoke(bs, false); - bdrv_drain_recurse(bs); aio_context_release(aio_context); } From 60369b86c427c6646c53b607b5a3e6b507ffe8d6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Dec 2017 11:00:59 +0100 Subject: [PATCH 08/35] block: Unify order in drain functions Drain requests are propagated to child nodes, parent nodes and directly to the AioContext. The order in which this happened was different between all combinations of drain/drain_all and begin/end. The correct order is to keep children only drained when their parents are also drained. This means that at the start of a drained section, the AioContext needs to be drained first, the parents second and only then the children. The correct order for the end of a drained section is the opposite. This patch changes the three other functions to follow the example of bdrv_drained_begin(), which is the only one that got it right. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/block/io.c b/block/io.c index 5fdb92a15e..1e92d2e5b2 100644 --- a/block/io.c +++ b/block/io.c @@ -277,6 +277,7 @@ void bdrv_drained_begin(BlockDriverState *bs) return; } + /* Stop things in parent-to-child order */ if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { aio_disable_external(bdrv_get_aio_context(bs)); bdrv_parent_drained_begin(bs); @@ -297,8 +298,9 @@ void bdrv_drained_end(BlockDriverState *bs) return; } - bdrv_parent_drained_end(bs); + /* Re-enable things in child-to-parent order */ bdrv_drain_invoke(bs, false); + bdrv_parent_drained_end(bs); aio_enable_external(bdrv_get_aio_context(bs)); } @@ -351,9 +353,10 @@ void bdrv_drain_all_begin(void) for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); + /* Stop things in parent-to-child order */ aio_context_acquire(aio_context); - bdrv_parent_drained_begin(bs); aio_disable_external(aio_context); + bdrv_parent_drained_begin(bs); bdrv_drain_invoke(bs, true); aio_context_release(aio_context); @@ -395,10 +398,11 @@ void bdrv_drain_all_end(void) for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); + /* Re-enable things in child-to-parent order */ aio_context_acquire(aio_context); - aio_enable_external(aio_context); - bdrv_parent_drained_end(bs); bdrv_drain_invoke(bs, false); + bdrv_parent_drained_end(bs); + aio_enable_external(aio_context); aio_context_release(aio_context); } From c200c4a470fc89d9a2b2d1884b140b03fd31981f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 11 Dec 2017 15:33:17 +0100 Subject: [PATCH 09/35] block: Don't acquire AioContext in hmp_qemu_io() Commit 15afd94a047 added code to acquire and release the AioContext in qemuio_command(). This means that the lock is taken twice now in the call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for any requests issued to nodes in a non-mainloop AioContext. Dropping the first locking from hmp_qemu_io() fixes the problem. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- hmp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hmp.c b/hmp.c index 35a7041824..2d72f94193 100644 --- a/hmp.c +++ b/hmp.c @@ -2318,7 +2318,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) { BlockBackend *blk; BlockBackend *local_blk = NULL; - AioContext *aio_context; const char* device = qdict_get_str(qdict, "device"); const char* command = qdict_get_str(qdict, "command"); Error *err = NULL; @@ -2338,9 +2337,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) } } - aio_context = blk_get_aio_context(blk); - aio_context_acquire(aio_context); - /* * Notably absent: Proper permission management. This is sad, but it seems * almost impossible to achieve without changing the semantics and thereby @@ -2368,8 +2364,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) */ qemuio_command(blk, command); - aio_context_release(aio_context); - fail: blk_unref(local_blk); hmp_handle_error(mon, &err); From 546a7dc40e8b8b6440a052e2b5cdfe9aadcaccf6 Mon Sep 17 00:00:00 2001 From: Edgar Kaziakhmedov Date: Tue, 12 Dec 2017 17:40:54 +0300 Subject: [PATCH 10/35] qcow2: get rid of qcow2_backing_read1 routine Since bdrv_co_preadv does all neccessary checks including reading after the end of the backing file, avoid duplication of verification before bdrv_co_preadv call. Signed-off-by: Edgar Kaziakhmedov Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/qcow2.c | 51 ++++++++------------------------------------------- block/qcow2.h | 3 --- 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 1914a940e5..4348b2c0c5 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1672,34 +1672,12 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, return status; } -/* handle reading after the end of the backing file */ -int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, - int64_t offset, int bytes) -{ - uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE; - int n1; - - if ((offset + bytes) <= bs_size) { - return bytes; - } - - if (offset >= bs_size) { - n1 = 0; - } else { - n1 = bs_size - offset; - } - - qemu_iovec_memset(qiov, n1, 0, bytes - n1); - - return n1; -} - static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { BDRVQcow2State *s = bs->opaque; - int offset_in_cluster, n1; + int offset_in_cluster; int ret; unsigned int cur_bytes; /* number of bytes in current iteration */ uint64_t cluster_offset = 0; @@ -1734,26 +1712,13 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, case QCOW2_CLUSTER_UNALLOCATED: if (bs->backing) { - /* read from the base image */ - n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov, - offset, cur_bytes); - if (n1 > 0) { - QEMUIOVector local_qiov; - - qemu_iovec_init(&local_qiov, hd_qiov.niov); - qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1); - - BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); - qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_preadv(bs->backing, offset, n1, - &local_qiov, 0); - qemu_co_mutex_lock(&s->lock); - - qemu_iovec_destroy(&local_qiov); - - if (ret < 0) { - goto fail; - } + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_preadv(bs->backing, offset, cur_bytes, + &hd_qiov, 0); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; } } else { /* Note: in this case, no need to wait */ diff --git a/block/qcow2.h b/block/qcow2.h index 6f0ff15dd0..46c8cf44ec 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -528,9 +528,6 @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset) } /* qcow2.c functions */ -int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, - int64_t sector_num, int nb_sectors); - int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, int refcount_order, bool generous_increase, uint64_t *refblock_count); From 6b4738ce4d32d551b37afb387813a37a24b6de8f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 15 Dec 2017 11:54:22 +0100 Subject: [PATCH 11/35] block: Document that x-blockdev-change breaks quorum children list Removing a quorum child node with x-blockdev-change results in a quorum driver state that cannot be recreated with create options because it would require a list with gaps. This causes trouble in at least .bdrv_refresh_filename(). Document this problem so that we won't accidentally mark the command stable without having addressed it. Signed-off-by: Kevin Wolf Reviewed-by: Alberto Garcia --- qapi/block-core.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qapi/block-core.json b/qapi/block-core.json index a8cdbc300b..e94a6881b2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3918,6 +3918,10 @@ # does not support all kinds of operations, all kinds of children, nor # all block drivers. # +# FIXME Removing children from a quorum node means introducing gaps in the +# child indices. This cannot be represented in the 'children' list of +# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename(). +# # Warning: The data in a new quorum child MUST be consistent with that of # the rest of the array. # From 1ee24514aed34760fb2863d98bea3a1b705d9c9f Mon Sep 17 00:00:00 2001 From: Doug Gale Date: Fri, 3 Nov 2017 09:37:53 -0400 Subject: [PATCH 12/35] nvme: Add tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add trace output for commands, errors, and undefined behavior. Add guest error log output for undefined behavior. Report invalid undefined accesses to MMIO. Annotate unlikely error checks with unlikely. Signed-off-by: Doug Gale Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- hw/block/nvme.c | 349 +++++++++++++++++++++++++++++++++++------- hw/block/trace-events | 93 +++++++++++ 2 files changed, 390 insertions(+), 52 deletions(-) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index e529e88e4e..1ac356d3a5 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -34,8 +34,17 @@ #include "qapi/visitor.h" #include "sysemu/block-backend.h" +#include "qemu/log.h" +#include "trace.h" #include "nvme.h" +#define NVME_GUEST_ERR(trace, fmt, ...) \ + do { \ + (trace_##trace)(__VA_ARGS__); \ + qemu_log_mask(LOG_GUEST_ERROR, #trace \ + " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \ + } while (0) + static void nvme_process_sq(void *opaque); static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) @@ -86,10 +95,14 @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq) { if (cq->irq_enabled) { if (msix_enabled(&(n->parent_obj))) { + trace_nvme_irq_msix(cq->vector); msix_notify(&(n->parent_obj), cq->vector); } else { + trace_nvme_irq_pin(); pci_irq_pulse(&n->parent_obj); } + } else { + trace_nvme_irq_masked(); } } @@ -100,7 +113,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, trans_len = MIN(len, trans_len); int num_prps = (len >> n->page_bits) + 1; - if (!prp1) { + if (unlikely(!prp1)) { + trace_nvme_err_invalid_prp(); return NVME_INVALID_FIELD | NVME_DNR; } else if (n->cmbsz && prp1 >= n->ctrl_mem.addr && prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) { @@ -113,7 +127,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, } len -= trans_len; if (len) { - if (!prp2) { + if (unlikely(!prp2)) { + trace_nvme_err_invalid_prp2_missing(); goto unmap; } if (len > n->page_size) { @@ -128,7 +143,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, uint64_t prp_ent = le64_to_cpu(prp_list[i]); if (i == n->max_prp_ents - 1 && len > n->page_size) { - if (!prp_ent || prp_ent & (n->page_size - 1)) { + if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) { + trace_nvme_err_invalid_prplist_ent(prp_ent); goto unmap; } @@ -140,7 +156,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, prp_ent = le64_to_cpu(prp_list[i]); } - if (!prp_ent || prp_ent & (n->page_size - 1)) { + if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) { + trace_nvme_err_invalid_prplist_ent(prp_ent); goto unmap; } @@ -154,7 +171,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, i++; } } else { - if (prp2 & (n->page_size - 1)) { + if (unlikely(prp2 & (n->page_size - 1))) { + trace_nvme_err_invalid_prp2_align(prp2); goto unmap; } if (qsg->nsg) { @@ -178,16 +196,20 @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len, QEMUIOVector iov; uint16_t status = NVME_SUCCESS; + trace_nvme_dma_read(prp1, prp2); + if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) { return NVME_INVALID_FIELD | NVME_DNR; } if (qsg.nsg > 0) { - if (dma_buf_read(ptr, len, &qsg)) { + if (unlikely(dma_buf_read(ptr, len, &qsg))) { + trace_nvme_err_invalid_dma(); status = NVME_INVALID_FIELD | NVME_DNR; } qemu_sglist_destroy(&qsg); } else { - if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) { + if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) { + trace_nvme_err_invalid_dma(); status = NVME_INVALID_FIELD | NVME_DNR; } qemu_iovec_destroy(&iov); @@ -273,7 +295,8 @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS); uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS); - if (slba + nlb > ns->id_ns.nsze) { + if (unlikely(slba + nlb > ns->id_ns.nsze)) { + trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); return NVME_LBA_RANGE | NVME_DNR; } @@ -301,8 +324,11 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0; enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ; - if ((slba + nlb) > ns->id_ns.nsze) { + trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba); + + if (unlikely((slba + nlb) > ns->id_ns.nsze)) { block_acct_invalid(blk_get_stats(n->conf.blk), acct); + trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); return NVME_LBA_RANGE | NVME_DNR; } @@ -336,7 +362,8 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) NvmeNamespace *ns; uint32_t nsid = le32_to_cpu(cmd->nsid); - if (nsid == 0 || nsid > n->num_namespaces) { + if (unlikely(nsid == 0 || nsid > n->num_namespaces)) { + trace_nvme_err_invalid_ns(nsid, n->num_namespaces); return NVME_INVALID_NSID | NVME_DNR; } @@ -350,6 +377,7 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) case NVME_CMD_READ: return nvme_rw(n, ns, cmd, req); default: + trace_nvme_err_invalid_opc(cmd->opcode); return NVME_INVALID_OPCODE | NVME_DNR; } } @@ -373,10 +401,13 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd) NvmeCQueue *cq; uint16_t qid = le16_to_cpu(c->qid); - if (!qid || nvme_check_sqid(n, qid)) { + if (unlikely(!qid || nvme_check_sqid(n, qid))) { + trace_nvme_err_invalid_del_sq(qid); return NVME_INVALID_QID | NVME_DNR; } + trace_nvme_del_sq(qid); + sq = n->sq[qid]; while (!QTAILQ_EMPTY(&sq->out_req_list)) { req = QTAILQ_FIRST(&sq->out_req_list); @@ -439,19 +470,26 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd) uint16_t qflags = le16_to_cpu(c->sq_flags); uint64_t prp1 = le64_to_cpu(c->prp1); - if (!cqid || nvme_check_cqid(n, cqid)) { + trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags); + + if (unlikely(!cqid || nvme_check_cqid(n, cqid))) { + trace_nvme_err_invalid_create_sq_cqid(cqid); return NVME_INVALID_CQID | NVME_DNR; } - if (!sqid || !nvme_check_sqid(n, sqid)) { + if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) { + trace_nvme_err_invalid_create_sq_sqid(sqid); return NVME_INVALID_QID | NVME_DNR; } - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { + if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) { + trace_nvme_err_invalid_create_sq_size(qsize); return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; } - if (!prp1 || prp1 & (n->page_size - 1)) { + if (unlikely(!prp1 || prp1 & (n->page_size - 1))) { + trace_nvme_err_invalid_create_sq_addr(prp1); return NVME_INVALID_FIELD | NVME_DNR; } - if (!(NVME_SQ_FLAGS_PC(qflags))) { + if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) { + trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags)); return NVME_INVALID_FIELD | NVME_DNR; } sq = g_malloc0(sizeof(*sq)); @@ -476,14 +514,17 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd) NvmeCQueue *cq; uint16_t qid = le16_to_cpu(c->qid); - if (!qid || nvme_check_cqid(n, qid)) { + if (unlikely(!qid || nvme_check_cqid(n, qid))) { + trace_nvme_err_invalid_del_cq_cqid(qid); return NVME_INVALID_CQID | NVME_DNR; } cq = n->cq[qid]; - if (!QTAILQ_EMPTY(&cq->sq_list)) { + if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) { + trace_nvme_err_invalid_del_cq_notempty(qid); return NVME_INVALID_QUEUE_DEL; } + trace_nvme_del_cq(qid); nvme_free_cq(cq, n); return NVME_SUCCESS; } @@ -516,19 +557,27 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd) uint16_t qflags = le16_to_cpu(c->cq_flags); uint64_t prp1 = le64_to_cpu(c->prp1); - if (!cqid || !nvme_check_cqid(n, cqid)) { + trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags, + NVME_CQ_FLAGS_IEN(qflags) != 0); + + if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) { + trace_nvme_err_invalid_create_cq_cqid(cqid); return NVME_INVALID_CQID | NVME_DNR; } - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { + if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) { + trace_nvme_err_invalid_create_cq_size(qsize); return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; } - if (!prp1) { + if (unlikely(!prp1)) { + trace_nvme_err_invalid_create_cq_addr(prp1); return NVME_INVALID_FIELD | NVME_DNR; } - if (vector > n->num_queues) { + if (unlikely(vector > n->num_queues)) { + trace_nvme_err_invalid_create_cq_vector(vector); return NVME_INVALID_IRQ_VECTOR | NVME_DNR; } - if (!(NVME_CQ_FLAGS_PC(qflags))) { + if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) { + trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags)); return NVME_INVALID_FIELD | NVME_DNR; } @@ -543,6 +592,8 @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c) uint64_t prp1 = le64_to_cpu(c->prp1); uint64_t prp2 = le64_to_cpu(c->prp2); + trace_nvme_identify_ctrl(); + return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), prp1, prp2); } @@ -554,11 +605,15 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c) uint64_t prp1 = le64_to_cpu(c->prp1); uint64_t prp2 = le64_to_cpu(c->prp2); - if (nsid == 0 || nsid > n->num_namespaces) { + trace_nvme_identify_ns(nsid); + + if (unlikely(nsid == 0 || nsid > n->num_namespaces)) { + trace_nvme_err_invalid_ns(nsid, n->num_namespaces); return NVME_INVALID_NSID | NVME_DNR; } ns = &n->namespaces[nsid - 1]; + return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns), prp1, prp2); } @@ -573,6 +628,8 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c) uint16_t ret; int i, j = 0; + trace_nvme_identify_nslist(min_nsid); + list = g_malloc0(data_len); for (i = 0; i < n->num_namespaces; i++) { if (i < min_nsid) { @@ -601,6 +658,7 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) case 0x02: return nvme_identify_nslist(n, c); default: + trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns)); return NVME_INVALID_FIELD | NVME_DNR; } } @@ -613,11 +671,14 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) switch (dw10) { case NVME_VOLATILE_WRITE_CACHE: result = blk_enable_write_cache(n->conf.blk); + trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled"); break; case NVME_NUMBER_OF_QUEUES: result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); + trace_nvme_getfeat_numq(result); break; default: + trace_nvme_err_invalid_getfeat(dw10); return NVME_INVALID_FIELD | NVME_DNR; } @@ -635,10 +696,14 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) blk_set_enable_write_cache(n->conf.blk, dw11 & 1); break; case NVME_NUMBER_OF_QUEUES: + trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1, + ((dw11 >> 16) & 0xFFFF) + 1, + n->num_queues - 1, n->num_queues - 1); req->cqe.result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); break; default: + trace_nvme_err_invalid_setfeat(dw10); return NVME_INVALID_FIELD | NVME_DNR; } return NVME_SUCCESS; @@ -662,6 +727,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) case NVME_ADM_CMD_GET_FEATURES: return nvme_get_feature(n, cmd, req); default: + trace_nvme_err_invalid_admin_opc(cmd->opcode); return NVME_INVALID_OPCODE | NVME_DNR; } } @@ -721,15 +787,78 @@ static int nvme_start_ctrl(NvmeCtrl *n) uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12; uint32_t page_size = 1 << page_bits; - if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq || - n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) || - NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) || - NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) || - NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) || - NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) || - NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) || - NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) || - !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) { + if (unlikely(n->cq[0])) { + trace_nvme_err_startfail_cq(); + return -1; + } + if (unlikely(n->sq[0])) { + trace_nvme_err_startfail_sq(); + return -1; + } + if (unlikely(!n->bar.asq)) { + trace_nvme_err_startfail_nbarasq(); + return -1; + } + if (unlikely(!n->bar.acq)) { + trace_nvme_err_startfail_nbaracq(); + return -1; + } + if (unlikely(n->bar.asq & (page_size - 1))) { + trace_nvme_err_startfail_asq_misaligned(n->bar.asq); + return -1; + } + if (unlikely(n->bar.acq & (page_size - 1))) { + trace_nvme_err_startfail_acq_misaligned(n->bar.acq); + return -1; + } + if (unlikely(NVME_CC_MPS(n->bar.cc) < + NVME_CAP_MPSMIN(n->bar.cap))) { + trace_nvme_err_startfail_page_too_small( + NVME_CC_MPS(n->bar.cc), + NVME_CAP_MPSMIN(n->bar.cap)); + return -1; + } + if (unlikely(NVME_CC_MPS(n->bar.cc) > + NVME_CAP_MPSMAX(n->bar.cap))) { + trace_nvme_err_startfail_page_too_large( + NVME_CC_MPS(n->bar.cc), + NVME_CAP_MPSMAX(n->bar.cap)); + return -1; + } + if (unlikely(NVME_CC_IOCQES(n->bar.cc) < + NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) { + trace_nvme_err_startfail_cqent_too_small( + NVME_CC_IOCQES(n->bar.cc), + NVME_CTRL_CQES_MIN(n->bar.cap)); + return -1; + } + if (unlikely(NVME_CC_IOCQES(n->bar.cc) > + NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) { + trace_nvme_err_startfail_cqent_too_large( + NVME_CC_IOCQES(n->bar.cc), + NVME_CTRL_CQES_MAX(n->bar.cap)); + return -1; + } + if (unlikely(NVME_CC_IOSQES(n->bar.cc) < + NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) { + trace_nvme_err_startfail_sqent_too_small( + NVME_CC_IOSQES(n->bar.cc), + NVME_CTRL_SQES_MIN(n->bar.cap)); + return -1; + } + if (unlikely(NVME_CC_IOSQES(n->bar.cc) > + NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) { + trace_nvme_err_startfail_sqent_too_large( + NVME_CC_IOSQES(n->bar.cc), + NVME_CTRL_SQES_MAX(n->bar.cap)); + return -1; + } + if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) { + trace_nvme_err_startfail_asqent_sz_zero(); + return -1; + } + if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) { + trace_nvme_err_startfail_acqent_sz_zero(); return -1; } @@ -749,16 +878,48 @@ static int nvme_start_ctrl(NvmeCtrl *n) static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, unsigned size) { + if (unlikely(offset & (sizeof(uint32_t) - 1))) { + NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32, + "MMIO write not 32-bit aligned," + " offset=0x%"PRIx64"", offset); + /* should be ignored, fall through for now */ + } + + if (unlikely(size < sizeof(uint32_t))) { + NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall, + "MMIO write smaller than 32-bits," + " offset=0x%"PRIx64", size=%u", + offset, size); + /* should be ignored, fall through for now */ + } + switch (offset) { - case 0xc: + case 0xc: /* INTMS */ + if (unlikely(msix_enabled(&(n->parent_obj)))) { + NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix, + "undefined access to interrupt mask set" + " when MSI-X is enabled"); + /* should be ignored, fall through for now */ + } n->bar.intms |= data & 0xffffffff; n->bar.intmc = n->bar.intms; + trace_nvme_mmio_intm_set(data & 0xffffffff, + n->bar.intmc); break; - case 0x10: + case 0x10: /* INTMC */ + if (unlikely(msix_enabled(&(n->parent_obj)))) { + NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix, + "undefined access to interrupt mask clr" + " when MSI-X is enabled"); + /* should be ignored, fall through for now */ + } n->bar.intms &= ~(data & 0xffffffff); n->bar.intmc = n->bar.intms; + trace_nvme_mmio_intm_clr(data & 0xffffffff, + n->bar.intmc); break; - case 0x14: + case 0x14: /* CC */ + trace_nvme_mmio_cfg(data & 0xffffffff); /* Windows first sends data, then sends enable bit */ if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) && !NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc)) @@ -768,40 +929,82 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) { n->bar.cc = data; - if (nvme_start_ctrl(n)) { + if (unlikely(nvme_start_ctrl(n))) { + trace_nvme_err_startfail(); n->bar.csts = NVME_CSTS_FAILED; } else { + trace_nvme_mmio_start_success(); n->bar.csts = NVME_CSTS_READY; } } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) { + trace_nvme_mmio_stopped(); nvme_clear_ctrl(n); n->bar.csts &= ~NVME_CSTS_READY; } if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) { - nvme_clear_ctrl(n); - n->bar.cc = data; - n->bar.csts |= NVME_CSTS_SHST_COMPLETE; + trace_nvme_mmio_shutdown_set(); + nvme_clear_ctrl(n); + n->bar.cc = data; + n->bar.csts |= NVME_CSTS_SHST_COMPLETE; } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) { - n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; - n->bar.cc = data; + trace_nvme_mmio_shutdown_cleared(); + n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; + n->bar.cc = data; } break; - case 0x24: + case 0x1C: /* CSTS */ + if (data & (1 << 4)) { + NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported, + "attempted to W1C CSTS.NSSRO" + " but CAP.NSSRS is zero (not supported)"); + } else if (data != 0) { + NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts, + "attempted to set a read only bit" + " of controller status"); + } + break; + case 0x20: /* NSSR */ + if (data == 0x4E564D65) { + trace_nvme_ub_mmiowr_ssreset_unsupported(); + } else { + /* The spec says that writes of other values have no effect */ + return; + } + break; + case 0x24: /* AQA */ n->bar.aqa = data & 0xffffffff; + trace_nvme_mmio_aqattr(data & 0xffffffff); break; - case 0x28: + case 0x28: /* ASQ */ n->bar.asq = data; + trace_nvme_mmio_asqaddr(data); break; - case 0x2c: + case 0x2c: /* ASQ hi */ n->bar.asq |= data << 32; + trace_nvme_mmio_asqaddr_hi(data, n->bar.asq); break; - case 0x30: + case 0x30: /* ACQ */ + trace_nvme_mmio_acqaddr(data); n->bar.acq = data; break; - case 0x34: + case 0x34: /* ACQ hi */ n->bar.acq |= data << 32; + trace_nvme_mmio_acqaddr_hi(data, n->bar.acq); break; + case 0x38: /* CMBLOC */ + NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved, + "invalid write to reserved CMBLOC" + " when CMBSZ is zero, ignored"); + return; + case 0x3C: /* CMBSZ */ + NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly, + "invalid write to read only CMBSZ, ignored"); + return; default: + NVME_GUEST_ERR(nvme_ub_mmiowr_invalid, + "invalid MMIO write," + " offset=0x%"PRIx64", data=%"PRIx64"", + offset, data); break; } } @@ -812,9 +1015,26 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) uint8_t *ptr = (uint8_t *)&n->bar; uint64_t val = 0; + if (unlikely(addr & (sizeof(uint32_t) - 1))) { + NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32, + "MMIO read not 32-bit aligned," + " offset=0x%"PRIx64"", addr); + /* should RAZ, fall through for now */ + } else if (unlikely(size < sizeof(uint32_t))) { + NVME_GUEST_ERR(nvme_ub_mmiord_toosmall, + "MMIO read smaller than 32-bits," + " offset=0x%"PRIx64"", addr); + /* should RAZ, fall through for now */ + } + if (addr < sizeof(n->bar)) { memcpy(&val, ptr + addr, size); + } else { + NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs, + "MMIO read beyond last register," + " offset=0x%"PRIx64", returning 0", addr); } + return val; } @@ -822,22 +1042,36 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) { uint32_t qid; - if (addr & ((1 << 2) - 1)) { + if (unlikely(addr & ((1 << 2) - 1))) { + NVME_GUEST_ERR(nvme_ub_db_wr_misaligned, + "doorbell write not 32-bit aligned," + " offset=0x%"PRIx64", ignoring", addr); return; } if (((addr - 0x1000) >> 2) & 1) { + /* Completion queue doorbell write */ + uint16_t new_head = val & 0xffff; int start_sqs; NvmeCQueue *cq; qid = (addr - (0x1000 + (1 << 2))) >> 3; - if (nvme_check_cqid(n, qid)) { + if (unlikely(nvme_check_cqid(n, qid))) { + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq, + "completion queue doorbell write" + " for nonexistent queue," + " sqid=%"PRIu32", ignoring", qid); return; } cq = n->cq[qid]; - if (new_head >= cq->size) { + if (unlikely(new_head >= cq->size)) { + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead, + "completion queue doorbell write value" + " beyond queue size, sqid=%"PRIu32"," + " new_head=%"PRIu16", ignoring", + qid, new_head); return; } @@ -855,16 +1089,27 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) nvme_isr_notify(n, cq); } } else { + /* Submission queue doorbell write */ + uint16_t new_tail = val & 0xffff; NvmeSQueue *sq; qid = (addr - 0x1000) >> 3; - if (nvme_check_sqid(n, qid)) { + if (unlikely(nvme_check_sqid(n, qid))) { + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq, + "submission queue doorbell write" + " for nonexistent queue," + " sqid=%"PRIu32", ignoring", qid); return; } sq = n->sq[qid]; - if (new_tail >= sq->size) { + if (unlikely(new_tail >= sq->size)) { + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail, + "submission queue doorbell write value" + " beyond queue size, sqid=%"PRIu32"," + " new_tail=%"PRIu16", ignoring", + qid, new_tail); return; } diff --git a/hw/block/trace-events b/hw/block/trace-events index 962a3bfa24..5acd495207 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -11,6 +11,99 @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6 hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d" hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d" +# hw/block/nvme.c +# nvme traces for successful events +nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u" +nvme_irq_pin(void) "pulsing IRQ pin" +nvme_irq_masked(void) "IRQ is masked" +nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64"" +nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64"" +nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16"" +nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d" +nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16"" +nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16"" +nvme_identify_ctrl(void) "identify controller" +nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16"" +nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16"" +nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s" +nvme_getfeat_numq(int result) "get feature number of queues, result=%d" +nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d" +nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64"" +nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64"" +nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64"" +nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64"" +nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64"" +nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64"" +nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64"" +nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64"" +nvme_mmio_start_success(void) "setting controller enable bit succeeded" +nvme_mmio_stopped(void) "cleared controller enable bit" +nvme_mmio_shutdown_set(void) "shutdown bit set" +nvme_mmio_shutdown_cleared(void) "shutdown bit cleared" + +# nvme traces for error conditions +nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size" +nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64"" +nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64"" +nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred" +nvme_err_invalid_field(void) "invalid field" +nvme_err_invalid_prp(void) "invalid PRP" +nvme_err_invalid_sgl(void) "invalid SGL" +nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u" +nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8"" +nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8"" +nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64"" +nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16"" +nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16"" +nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16"" +nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16"" +nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64"" +nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16"" +nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16"" +nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16"" +nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16"" +nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16"" +nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64"" +nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16"" +nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16"" +nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16"" +nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32"" +nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32"" +nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues" +nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues" +nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null" +nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null" +nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64"" +nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64"" +nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u" +nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u" +nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u" +nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u" +nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u" +nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u" +nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero" +nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero" +nvme_err_startfail(void) "setting controller enable bit failed" + +# Traces for undefined behavior +nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64"" +nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u" +nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled" +nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status" +nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)" +nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)" +nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored" +nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored" +nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64"" +nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64"" +nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64"" +nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0" +nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring" +nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring" +nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring" +nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring" +nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring" + # hw/block/xen_disk.c xen_disk_alloc(char *name) "%s" xen_disk_init(char *name) "%s" From cc954f01e3c004aad081aa36736a17e842b80211 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 15 Dec 2017 16:04:45 +0800 Subject: [PATCH 13/35] block: Open backing image in force share mode for size probe Management tools create overlays of running guests with qemu-img: $ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2 but this doesn't work anymore due to image locking: qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock Is another process using the image? Could not open backing image to determine size. Use the force share option to allow this use case again. Cc: qemu-stable@nongnu.org Signed-off-by: Fam Zheng Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block.c b/block.c index db0be7ef36..dd90dca896 100644 --- a/block.c +++ b/block.c @@ -4605,10 +4605,11 @@ void bdrv_img_create(const char *filename, const char *fmt, back_flags = flags; back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + backing_options = qdict_new(); if (backing_fmt) { - backing_options = qdict_new(); qdict_put_str(backing_options, "driver", backing_fmt); } + qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true); bs = bdrv_open(full_backing, NULL, backing_options, back_flags, &local_err); From 0e153b04cccaeaa272a687194ea353167878b10f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 18 Dec 2017 18:14:31 +0100 Subject: [PATCH 14/35] block: Remove the obsolete -drive boot=on|off parameter It's not working anymore since QEMU v1.3.0 - time to remove it now. Signed-off-by: Thomas Huth Reviewed-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Kevin Wolf --- blockdev.c | 11 ----------- qemu-doc.texi | 6 ------ 2 files changed, 17 deletions(-) diff --git a/blockdev.c b/blockdev.c index 9c3a430cfb..29d569a24e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -734,10 +734,6 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "trans", .type = QEMU_OPT_STRING, .help = "chs translation (auto, lba, none)", - },{ - .name = "boot", - .type = QEMU_OPT_BOOL, - .help = "(deprecated, ignored)", },{ .name = "addr", .type = QEMU_OPT_STRING, @@ -873,13 +869,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) goto fail; } - /* Deprecated option boot=[on|off] */ - if (qemu_opt_get(legacy_opts, "boot") != NULL) { - fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be " - "ignored. Future versions will reject this parameter. Please " - "update your scripts.\n"); - } - /* Other deprecated options */ if (!qtest_enabled()) { for (i = 0; i < ARRAY_SIZE(deprecated); i++) { diff --git a/qemu-doc.texi b/qemu-doc.texi index 90bea7331d..ed55bd477d 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2589,12 +2589,6 @@ deprecated. @section System emulator command line arguments -@subsection -drive boot=on|off (since 1.3.0) - -The ``boot=on|off'' option to the ``-drive'' argument is -ignored. Applications should use the ``bootindex=N'' parameter -to set an absolute ordering between devices instead. - @subsection -tdf (since 1.3.0) The ``-tdf'' argument is ignored. The behaviour implemented From d1cdd92e5c67d14de6f8ea604715d24b6370b442 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 18 Dec 2017 18:14:32 +0100 Subject: [PATCH 15/35] block: Remove the deprecated -hdachs option It's been marked as deprecated since QEMU v2.10.0, and so far nobody complained that we should keep it, so let's remove this legacy option now to simplify the code quite a bit. Signed-off-by: Thomas Huth Reviewed-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Kevin Wolf --- qemu-doc.texi | 8 ----- qemu-options.hx | 19 ++--------- vl.c | 86 ++----------------------------------------------- 3 files changed, 4 insertions(+), 109 deletions(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index ed55bd477d..96fda9c56e 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2686,14 +2686,6 @@ The ``--net dump'' argument is now replaced with the ``-object filter-dump'' argument which works in combination with the modern ``-netdev`` backends instead. -@subsection -hdachs (since 2.10.0) - -The ``-hdachs'' argument is now a synonym for setting -the ``cyls'', ``heads'', ``secs'', and ``trans'' properties -on the ``ide-hd'' device using the ``-device'' argument. -The new syntax allows different settings to be provided -per disk. - @subsection -usbdevice (since 2.10.0) The ``-usbdevice DEV'' argument is now a synonym for setting diff --git a/qemu-options.hx b/qemu-options.hx index 94647e21e3..2d39ba0b6d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -846,8 +846,8 @@ of available connectors of a given interface type. @item media=@var{media} This option defines the type of the media: disk or cdrom. @item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] -These options have the same definition as they have in @option{-hdachs}. -These parameters are deprecated, use the corresponding parameters +Force disk physical geometry and the optional BIOS translation (trans=none or +lba). These parameters are deprecated, use the corresponding parameters of @code{-device} instead. @item snapshot=@var{snapshot} @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive @@ -1027,21 +1027,6 @@ the raw disk image you use is not written back. You can however force the write back by pressing @key{C-a s} (@pxref{disk_images}). ETEXI -DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \ - "-hdachs c,h,s[,t]\n" \ - " force hard disk 0 physical geometry and the optional BIOS\n" \ - " translation (t=none or lba) (usually QEMU can guess them)\n", - QEMU_ARCH_ALL) -STEXI -@item -hdachs @var{c},@var{h},@var{s},[,@var{t}] -@findex -hdachs -Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <= -@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS -translation mode (@var{t}=none, lba or auto). Usually QEMU can guess -all those parameters. This option is deprecated, please use -@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead. -ETEXI - DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n" " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n" diff --git a/vl.c b/vl.c index d3a5c5d021..444b7507da 100644 --- a/vl.c +++ b/vl.c @@ -3052,9 +3052,8 @@ int main(int argc, char **argv, char **envp) const char *boot_order = NULL; const char *boot_once = NULL; DisplayState *ds; - int cyls, heads, secs, translation; QemuOpts *opts, *machine_opts; - QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL; + QemuOpts *icount_opts = NULL, *accel_opts = NULL; QemuOptsList *olist; int optind; const char *optarg; @@ -3146,8 +3145,6 @@ int main(int argc, char **argv, char **envp) cpu_model = NULL; snapshot = 0; - cyls = heads = secs = 0; - translation = BIOS_ATA_TRANSLATION_AUTO; nb_nics = 0; @@ -3186,7 +3183,7 @@ int main(int argc, char **argv, char **envp) if (optind >= argc) break; if (argv[optind][0] != '-') { - hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); + drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); } else { const QEMUOption *popt; @@ -3206,21 +3203,6 @@ int main(int argc, char **argv, char **envp) cpu_model = optarg; break; case QEMU_OPTION_hda: - { - char buf[256]; - if (cyls == 0) - snprintf(buf, sizeof(buf), "%s", HD_OPTS); - else - snprintf(buf, sizeof(buf), - "%s,cyls=%d,heads=%d,secs=%d%s", - HD_OPTS , cyls, heads, secs, - translation == BIOS_ATA_TRANSLATION_LBA ? - ",trans=lba" : - translation == BIOS_ATA_TRANSLATION_NONE ? - ",trans=none" : ""); - drive_add(IF_DEFAULT, 0, optarg, buf); - break; - } case QEMU_OPTION_hdb: case QEMU_OPTION_hdc: case QEMU_OPTION_hdd: @@ -3271,70 +3253,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_snapshot: snapshot = 1; break; - case QEMU_OPTION_hdachs: - { - const char *p; - p = optarg; - cyls = strtol(p, (char **)&p, 0); - if (cyls < 1 || cyls > 16383) - goto chs_fail; - if (*p != ',') - goto chs_fail; - p++; - heads = strtol(p, (char **)&p, 0); - if (heads < 1 || heads > 16) - goto chs_fail; - if (*p != ',') - goto chs_fail; - p++; - secs = strtol(p, (char **)&p, 0); - if (secs < 1 || secs > 63) - goto chs_fail; - if (*p == ',') { - p++; - if (!strcmp(p, "large")) { - translation = BIOS_ATA_TRANSLATION_LARGE; - } else if (!strcmp(p, "rechs")) { - translation = BIOS_ATA_TRANSLATION_RECHS; - } else if (!strcmp(p, "none")) { - translation = BIOS_ATA_TRANSLATION_NONE; - } else if (!strcmp(p, "lba")) { - translation = BIOS_ATA_TRANSLATION_LBA; - } else if (!strcmp(p, "auto")) { - translation = BIOS_ATA_TRANSLATION_AUTO; - } else { - goto chs_fail; - } - } else if (*p != '\0') { - chs_fail: - error_report("invalid physical CHS format"); - exit(1); - } - if (hda_opts != NULL) { - qemu_opt_set_number(hda_opts, "cyls", cyls, - &error_abort); - qemu_opt_set_number(hda_opts, "heads", heads, - &error_abort); - qemu_opt_set_number(hda_opts, "secs", secs, - &error_abort); - if (translation == BIOS_ATA_TRANSLATION_LARGE) { - qemu_opt_set(hda_opts, "trans", "large", - &error_abort); - } else if (translation == BIOS_ATA_TRANSLATION_RECHS) { - qemu_opt_set(hda_opts, "trans", "rechs", - &error_abort); - } else if (translation == BIOS_ATA_TRANSLATION_LBA) { - qemu_opt_set(hda_opts, "trans", "lba", - &error_abort); - } else if (translation == BIOS_ATA_TRANSLATION_NONE) { - qemu_opt_set(hda_opts, "trans", "none", - &error_abort); - } - } - } - error_report("'-hdachs' is deprecated, please use '-device" - " ide-hd,cyls=c,heads=h,secs=s,...' instead"); - break; case QEMU_OPTION_numa: opts = qemu_opts_parse_noisily(qemu_find_opts("numa"), optarg, true); From c08d46a96f218e3f2c6f4d0e590d267a9fd77a3a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 18 Dec 2017 18:14:33 +0100 Subject: [PATCH 16/35] block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter Looks like we forgot to announce the deprecation of these options in the corresponding chapter of the qemu-doc text, so let's do that now. Signed-off-by: Thomas Huth Reviewed-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Kevin Wolf --- qemu-doc.texi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/qemu-doc.texi b/qemu-doc.texi index 96fda9c56e..2cc741e77e 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2680,6 +2680,21 @@ longer be directly supported in QEMU. The ``-drive if=scsi'' argument is replaced by the the ``-device BUS-TYPE'' argument combined with ``-drive if=none''. +@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) + +The drive geometry arguments are replaced by the the geometry arguments +that can be specified with the ``-device'' parameter. + +@subsection -drive serial=... (since 2.10.0) + +The drive serial argument is replaced by the the serial argument +that can be specified with the ``-device'' parameter. + +@subsection -drive addr=... (since 2.10.0) + +The drive addr argument is replaced by the the addr argument +that can be specified with the ``-device'' parameter. + @subsection -net dump (since 2.10.0) The ``--net dump'' argument is now replaced with the From 8e77e0bceb4de0fe5cc912d5865b28a703f0f041 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 29 Nov 2017 22:49:48 +0800 Subject: [PATCH 17/35] block: Remove unused bdrv_requests_pending Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 18 ------------------ include/block/block_int.h | 1 - 2 files changed, 19 deletions(-) diff --git a/block/io.c b/block/io.c index 1e92d2e5b2..cf780c3cb0 100644 --- a/block/io.c +++ b/block/io.c @@ -134,24 +134,6 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs) assert(old >= 1); } -/* Check if any requests are in-flight (including throttled requests) */ -bool bdrv_requests_pending(BlockDriverState *bs) -{ - BdrvChild *child; - - if (atomic_read(&bs->in_flight)) { - return true; - } - - QLIST_FOREACH(child, &bs->children, next) { - if (bdrv_requests_pending(child->bs)) { - return true; - } - } - - return false; -} - typedef struct { Coroutine *co; BlockDriverState *bs; diff --git a/include/block/block_int.h b/include/block/block_int.h index a5482775ec..e107163594 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1045,7 +1045,6 @@ bool blk_dev_is_tray_open(BlockBackend *blk); bool blk_dev_is_medium_locked(BlockBackend *blk); void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); -bool bdrv_requests_pending(BlockDriverState *bs); void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); From 9a7e86c8048cecededa665b1ca55c7f217ed358c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 15 Dec 2017 09:33:21 +0100 Subject: [PATCH 18/35] block: Assert drain_all is only called from main AioContext Signed-off-by: Kevin Wolf Reviewed-by: Fam Zheng --- block/io.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/block/io.c b/block/io.c index cf780c3cb0..b94740b8ff 100644 --- a/block/io.c +++ b/block/io.c @@ -330,6 +330,12 @@ void bdrv_drain_all_begin(void) BdrvNextIterator it; GSList *aio_ctxs = NULL, *ctx; + /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread + * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on + * nodes in several different AioContexts, so make sure we're in the main + * context. */ + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + block_job_pause_all(); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { From 7b6a3d35536f945c41aa62627cc295482606aa2e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 7 Dec 2017 12:20:10 +0100 Subject: [PATCH 19/35] block: Make bdrv_drain() driver callbacks non-recursive bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively and also doesn't notify other parent nodes of children, which both means that the child nodes are not actually drained, and bdrv_drained_begin() is providing useful functionality only on a single node. To keep things consistent, we also shouldn't call the block driver callbacks recursively. A proper recursive drain version that provides an actually working drained section for child nodes will be introduced later. Signed-off-by: Kevin Wolf Reviewed-by: Fam Zheng --- block/io.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/block/io.c b/block/io.c index b94740b8ff..91a52e2d82 100644 --- a/block/io.c +++ b/block/io.c @@ -158,7 +158,7 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) } /* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ -static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) +static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive) { BdrvChild *child, *tmp; BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; @@ -172,8 +172,10 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) bdrv_coroutine_enter(bs, data.co); BDRV_POLL_WHILE(bs, !data.done); - QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { - bdrv_drain_invoke(child->bs, begin); + if (recursive) { + QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { + bdrv_drain_invoke(child->bs, begin, true); + } } } @@ -265,7 +267,7 @@ void bdrv_drained_begin(BlockDriverState *bs) bdrv_parent_drained_begin(bs); } - bdrv_drain_invoke(bs, true); + bdrv_drain_invoke(bs, true, false); bdrv_drain_recurse(bs); } @@ -281,7 +283,7 @@ void bdrv_drained_end(BlockDriverState *bs) } /* Re-enable things in child-to-parent order */ - bdrv_drain_invoke(bs, false); + bdrv_drain_invoke(bs, false, false); bdrv_parent_drained_end(bs); aio_enable_external(bdrv_get_aio_context(bs)); } @@ -345,7 +347,7 @@ void bdrv_drain_all_begin(void) aio_context_acquire(aio_context); aio_disable_external(aio_context); bdrv_parent_drained_begin(bs); - bdrv_drain_invoke(bs, true); + bdrv_drain_invoke(bs, true, true); aio_context_release(aio_context); if (!g_slist_find(aio_ctxs, aio_context)) { @@ -388,7 +390,7 @@ void bdrv_drain_all_end(void) /* Re-enable things in child-to-parent order */ aio_context_acquire(aio_context); - bdrv_drain_invoke(bs, false); + bdrv_drain_invoke(bs, false, true); bdrv_parent_drained_end(bs); aio_enable_external(aio_context); aio_context_release(aio_context); From 86e1c840ec1173badfe9f195a448c88072b2ad2a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Dec 2017 18:13:53 +0100 Subject: [PATCH 20/35] test-bdrv-drain: Test callback for bdrv_drain The existing test is for bdrv_drain_all_begin/end() only. Generalise the test case so that it can be run for the other variants as well. At the moment this is only bdrv_drain_begin/end(), but in a while, we'll add another one. Also, add a backing file to the test node to test whether the operations work recursively. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 71 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 0e567e34ba..3a3eef0f87 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -71,6 +71,8 @@ static BlockDriver bdrv_test = { .bdrv_co_drain_begin = bdrv_test_co_drain_begin, .bdrv_co_drain_end = bdrv_test_co_drain_end, + + .bdrv_child_perm = bdrv_format_default_perms, }; static void aio_ret_cb(void *opaque, int ret) @@ -79,11 +81,34 @@ static void aio_ret_cb(void *opaque, int ret) *aio_ret = ret; } -static void test_drv_cb_drain_all(void) +enum drain_type { + BDRV_DRAIN_ALL, + BDRV_DRAIN, +}; + +static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) +{ + switch (drain_type) { + case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break; + case BDRV_DRAIN: bdrv_drained_begin(bs); break; + default: g_assert_not_reached(); + } +} + +static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) +{ + switch (drain_type) { + case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break; + case BDRV_DRAIN: bdrv_drained_end(bs); break; + default: g_assert_not_reached(); + } +} + +static void test_drv_cb_common(enum drain_type drain_type, bool recursive) { BlockBackend *blk; - BlockDriverState *bs; - BDRVTestState *s; + BlockDriverState *bs, *backing; + BDRVTestState *s, *backing_s; BlockAIOCB *acb; int aio_ret; @@ -100,12 +125,23 @@ static void test_drv_cb_drain_all(void) s = bs->opaque; blk_insert_bs(blk, bs, &error_abort); + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs, backing, &error_abort); + /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ g_assert_cmpint(s->drain_count, ==, 0); - bdrv_drain_all_begin(); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(drain_type, bs); + g_assert_cmpint(s->drain_count, ==, 1); - bdrv_drain_all_end(); + g_assert_cmpint(backing_s->drain_count, ==, !!recursive); + + do_drain_end(drain_type, bs); + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); /* Now do the same while a request is pending */ aio_ret = -EINPROGRESS; @@ -114,16 +150,34 @@ static void test_drv_cb_drain_all(void) g_assert_cmpint(aio_ret, ==, -EINPROGRESS); g_assert_cmpint(s->drain_count, ==, 0); - bdrv_drain_all_begin(); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(drain_type, bs); + g_assert_cmpint(aio_ret, ==, 0); g_assert_cmpint(s->drain_count, ==, 1); - bdrv_drain_all_end(); - g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, !!recursive); + do_drain_end(drain_type, bs); + + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + bdrv_unref(backing); bdrv_unref(bs); blk_unref(blk); } +static void test_drv_cb_drain_all(void) +{ + test_drv_cb_common(BDRV_DRAIN_ALL, true); +} + +static void test_drv_cb_drain(void) +{ + test_drv_cb_common(BDRV_DRAIN, false); +} + int main(int argc, char **argv) { bdrv_init(); @@ -132,6 +186,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); + g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); return g_test_run(); } From 89a6ceab4617fefa5ace6a46be3e2be58925ae71 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 7 Dec 2017 17:00:22 +0100 Subject: [PATCH 21/35] test-bdrv-drain: Test bs->quiesce_counter This is currently only working correctly for bdrv_drain(), not for bdrv_drain_all(). Leave a comment for the drain_all case, we'll address it later. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 3a3eef0f87..323cb0b961 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -178,6 +178,48 @@ static void test_drv_cb_drain(void) test_drv_cb_common(BDRV_DRAIN, false); } +static void test_quiesce_common(enum drain_type drain_type, bool recursive) +{ + BlockBackend *blk; + BlockDriverState *bs, *backing; + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + blk_insert_bs(blk, bs, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + bdrv_set_backing_hd(bs, backing, &error_abort); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + + do_drain_begin(drain_type, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, 1); + g_assert_cmpint(backing->quiesce_counter, ==, !!recursive); + + do_drain_end(drain_type, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + + bdrv_unref(backing); + bdrv_unref(bs); + blk_unref(blk); +} + +static void test_quiesce_drain_all(void) +{ + // XXX drain_all doesn't quiesce + //test_quiesce_common(BDRV_DRAIN_ALL, true); +} + +static void test_quiesce_drain(void) +{ + test_quiesce_common(BDRV_DRAIN, false); +} + int main(int argc, char **argv) { bdrv_init(); @@ -188,5 +230,8 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); + g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); + g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); + return g_test_run(); } From ad90febaf22d95e49fb6821bfb3ebd05b4919417 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 12 Dec 2017 19:04:28 +0100 Subject: [PATCH 22/35] blockjob: Pause job on draining any job BDS Block jobs already paused themselves when their main BlockBackend entered a drained section. This is not good enough: We also want to pause a block job and may not submit new requests if, for example, the mirror target node should be drained. This implements .drained_begin/end callbacks in child_job in order to consider all block nodes related to the job, and removes the BlockBackend callbacks which are unnecessary now because the root of the job main BlockBackend is always referenced with a child_job, too. Signed-off-by: Kevin Wolf --- blockjob.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/blockjob.c b/blockjob.c index 6173e4728c..f5cea84e73 100644 --- a/blockjob.c +++ b/blockjob.c @@ -234,26 +234,23 @@ static char *child_job_get_parent_desc(BdrvChild *c) job->id); } -static const BdrvChildRole child_job = { - .get_parent_desc = child_job_get_parent_desc, - .stay_at_node = true, -}; - -static void block_job_drained_begin(void *opaque) +static void child_job_drained_begin(BdrvChild *c) { - BlockJob *job = opaque; + BlockJob *job = c->opaque; block_job_pause(job); } -static void block_job_drained_end(void *opaque) +static void child_job_drained_end(BdrvChild *c) { - BlockJob *job = opaque; + BlockJob *job = c->opaque; block_job_resume(job); } -static const BlockDevOps block_job_dev_ops = { - .drained_begin = block_job_drained_begin, - .drained_end = block_job_drained_end, +static const BdrvChildRole child_job = { + .get_parent_desc = child_job_get_parent_desc, + .drained_begin = child_job_drained_begin, + .drained_end = child_job_drained_end, + .stay_at_node = true, }; void block_job_remove_all_bdrv(BlockJob *job) @@ -715,7 +712,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); bs->job = job; - blk_set_dev_ops(blk, &block_job_dev_ops, job); bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); QLIST_INSERT_HEAD(&block_jobs, job, job_list); From 7253220de42e82c59e72f29e69285a9a9e9e96ee Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 12 Dec 2017 19:10:19 +0100 Subject: [PATCH 23/35] test-bdrv-drain: Test drain vs. block jobs Block jobs must be paused if any of the involved nodes are drained. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 323cb0b961..9783768a20 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "block/block.h" +#include "block/blockjob_int.h" #include "sysemu/block-backend.h" #include "qapi/error.h" @@ -220,6 +221,123 @@ static void test_quiesce_drain(void) test_quiesce_common(BDRV_DRAIN, false); } + +typedef struct TestBlockJob { + BlockJob common; + bool should_complete; +} TestBlockJob; + +static void test_job_completed(BlockJob *job, void *opaque) +{ + block_job_completed(job, 0); +} + +static void coroutine_fn test_job_start(void *opaque) +{ + TestBlockJob *s = opaque; + + while (!s->should_complete) { + block_job_sleep_ns(&s->common, 100000); + } + + block_job_defer_to_main_loop(&s->common, test_job_completed, NULL); +} + +static void test_job_complete(BlockJob *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common); + s->should_complete = true; +} + +BlockJobDriver test_job_driver = { + .instance_size = sizeof(TestBlockJob), + .start = test_job_start, + .complete = test_job_complete, +}; + +static void test_blockjob_common(enum drain_type drain_type) +{ + BlockBackend *blk_src, *blk_target; + BlockDriverState *src, *target; + BlockJob *job; + int ret; + + src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, + &error_abort); + blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk_src, src, &error_abort); + + target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, + &error_abort); + blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk_target, target, &error_abort); + + job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0, + 0, NULL, NULL, &error_abort); + block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); + block_job_start(job); + + g_assert_cmpint(job->pause_count, ==, 0); + g_assert_false(job->paused); + g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + + do_drain_begin(drain_type, src); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target, and involves an + * additional block_job_pause_all() */ + g_assert_cmpint(job->pause_count, ==, 3); + } else { + g_assert_cmpint(job->pause_count, ==, 1); + } + /* XXX We don't wait until the job is actually paused. Is this okay? */ + /* g_assert_true(job->paused); */ + g_assert_false(job->busy); /* The job is paused */ + + do_drain_end(drain_type, src); + + g_assert_cmpint(job->pause_count, ==, 0); + g_assert_false(job->paused); + g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + + do_drain_begin(drain_type, target); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target, and involves an + * additional block_job_pause_all() */ + g_assert_cmpint(job->pause_count, ==, 3); + } else { + g_assert_cmpint(job->pause_count, ==, 1); + } + /* XXX We don't wait until the job is actually paused. Is this okay? */ + /* g_assert_true(job->paused); */ + g_assert_false(job->busy); /* The job is paused */ + + do_drain_end(drain_type, target); + + g_assert_cmpint(job->pause_count, ==, 0); + g_assert_false(job->paused); + g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + + ret = block_job_complete_sync(job, &error_abort); + g_assert_cmpint(ret, ==, 0); + + blk_unref(blk_src); + blk_unref(blk_target); + bdrv_unref(src); + bdrv_unref(target); +} + +static void test_blockjob_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL); +} + +static void test_blockjob_drain(void) +{ + test_blockjob_common(BDRV_DRAIN); +} + int main(int argc, char **argv) { bdrv_init(); @@ -233,5 +351,8 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); + g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); + g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); + return g_test_run(); } From 8119334918e86f45877cfc139192d54f2449a239 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 14 Dec 2017 10:12:42 +0100 Subject: [PATCH 24/35] block: Don't block_job_pause_all() in bdrv_drain_all() Block jobs are already paused using the BdrvChildRole drain callbacks, so we don't need an additional block_job_pause_all() call. Signed-off-by: Kevin Wolf --- block/io.c | 4 ---- tests/test-bdrv-drain.c | 10 ++++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/block/io.c b/block/io.c index 91a52e2d82..74d2e5278e 100644 --- a/block/io.c +++ b/block/io.c @@ -338,8 +338,6 @@ void bdrv_drain_all_begin(void) * context. */ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - block_job_pause_all(); - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); @@ -395,8 +393,6 @@ void bdrv_drain_all_end(void) aio_enable_external(aio_context); aio_context_release(aio_context); } - - block_job_resume_all(); } void bdrv_drain_all(void) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 9783768a20..6da66ae841 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -284,9 +284,8 @@ static void test_blockjob_common(enum drain_type drain_type) do_drain_begin(drain_type, src); if (drain_type == BDRV_DRAIN_ALL) { - /* bdrv_drain_all() drains both src and target, and involves an - * additional block_job_pause_all() */ - g_assert_cmpint(job->pause_count, ==, 3); + /* bdrv_drain_all() drains both src and target */ + g_assert_cmpint(job->pause_count, ==, 2); } else { g_assert_cmpint(job->pause_count, ==, 1); } @@ -303,9 +302,8 @@ static void test_blockjob_common(enum drain_type drain_type) do_drain_begin(drain_type, target); if (drain_type == BDRV_DRAIN_ALL) { - /* bdrv_drain_all() drains both src and target, and involves an - * additional block_job_pause_all() */ - g_assert_cmpint(job->pause_count, ==, 3); + /* bdrv_drain_all() drains both src and target */ + g_assert_cmpint(job->pause_count, ==, 2); } else { g_assert_cmpint(job->pause_count, ==, 1); } From 0f115168943e5bf2219497abfbf5f7a9c271b9b0 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Dec 2017 18:14:18 +0100 Subject: [PATCH 25/35] block: Nested drain_end must still call callbacks bdrv_do_drained_begin() restricts the call of parent callbacks and aio_disable_external() to the outermost drain section, but the block driver callbacks are always called. bdrv_do_drained_end() must match this behaviour, otherwise nodes stay drained even if begin/end calls were balanced. Signed-off-by: Kevin Wolf --- block/io.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/block/io.c b/block/io.c index 74d2e5278e..6038a16c58 100644 --- a/block/io.c +++ b/block/io.c @@ -273,19 +273,21 @@ void bdrv_drained_begin(BlockDriverState *bs) void bdrv_drained_end(BlockDriverState *bs) { + int old_quiesce_counter; + if (qemu_in_coroutine()) { bdrv_co_yield_to_drain(bs, false); return; } assert(bs->quiesce_counter > 0); - if (atomic_fetch_dec(&bs->quiesce_counter) > 1) { - return; - } + old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter); /* Re-enable things in child-to-parent order */ bdrv_drain_invoke(bs, false, false); - bdrv_parent_drained_end(bs); - aio_enable_external(bdrv_get_aio_context(bs)); + if (old_quiesce_counter == 1) { + bdrv_parent_drained_end(bs); + aio_enable_external(bdrv_get_aio_context(bs)); + } } /* From 6c429a6a97d183aeb2c00f8680aaf3d9e900359d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Dec 2017 18:14:49 +0100 Subject: [PATCH 26/35] test-bdrv-drain: Test nested drain sections Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 6da66ae841..9098b77ab4 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -85,6 +85,7 @@ static void aio_ret_cb(void *opaque, int ret) enum drain_type { BDRV_DRAIN_ALL, BDRV_DRAIN, + DRAIN_TYPE_MAX, }; static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) @@ -221,6 +222,60 @@ static void test_quiesce_drain(void) test_quiesce_common(BDRV_DRAIN, false); } +static void test_nested(void) +{ + BlockBackend *blk; + BlockDriverState *bs, *backing; + BDRVTestState *s, *backing_s; + enum drain_type outer, inner; + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + s = bs->opaque; + blk_insert_bs(blk, bs, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs, backing, &error_abort); + + for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { + for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { + /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */ + int bs_quiesce = (outer != BDRV_DRAIN_ALL) + + (inner != BDRV_DRAIN_ALL); + int backing_quiesce = 0; + int backing_cb_cnt = (outer != BDRV_DRAIN) + + (inner != BDRV_DRAIN); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(outer, bs); + do_drain_begin(inner, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce); + g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); + g_assert_cmpint(s->drain_count, ==, 2); + g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt); + + do_drain_end(inner, bs); + do_drain_end(outer, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + } + } + + bdrv_unref(backing); + bdrv_unref(bs); + blk_unref(blk); +} + typedef struct TestBlockJob { BlockJob common; @@ -349,6 +404,8 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); + g_test_add_func("/bdrv-drain/nested", test_nested); + g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); From 0152bf400fe3ca7facb382683bfcccda70ebf51a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 7 Dec 2017 13:03:13 +0100 Subject: [PATCH 27/35] block: Don't notify parents in drain call chain This is in preparation for subtree drains, i.e. drained sections that affect not only a single node, but recursively all child nodes, too. Calling the parent callbacks for drain is pointless when we just came from that parent node recursively and leads to multiple increases of bs->quiesce_counter in a single drain call. Don't do it. In order for this to work correctly, the parent callback must be called for every bdrv_drain_begin/end() call, not only for the outermost one: If we have a node N with two parents A and B, recursive draining of A should cause the quiesce_counter of B to increase because its child N is drained independently of B. If now B is recursively drained, too, A must increase its quiesce_counter because N is drained independently of A only now, even if N is going from quiesce_counter 1 to 2. Signed-off-by: Kevin Wolf --- block.c | 13 ++++++++---- block/io.c | 47 +++++++++++++++++++++++++++++++------------ include/block/block.h | 4 ++-- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/block.c b/block.c index dd90dca896..6c247167b8 100644 --- a/block.c +++ b/block.c @@ -1972,13 +1972,16 @@ static void bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) { BlockDriverState *old_bs = child->bs; + int i; if (old_bs && new_bs) { assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); } if (old_bs) { if (old_bs->quiesce_counter && child->role->drained_end) { - child->role->drained_end(child); + for (i = 0; i < old_bs->quiesce_counter; i++) { + child->role->drained_end(child); + } } if (child->role->detach) { child->role->detach(child); @@ -1991,7 +1994,9 @@ static void bdrv_replace_child_noperm(BdrvChild *child, if (new_bs) { QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); if (new_bs->quiesce_counter && child->role->drained_begin) { - child->role->drained_begin(child); + for (i = 0; i < new_bs->quiesce_counter; i++) { + child->role->drained_begin(child); + } } if (child->role->attach) { @@ -4759,7 +4764,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) AioContext *ctx = bdrv_get_aio_context(bs); aio_disable_external(ctx); - bdrv_parent_drained_begin(bs); + bdrv_parent_drained_begin(bs, NULL); bdrv_drain(bs); /* ensure there are no in-flight requests */ while (aio_poll(ctx, false)) { @@ -4773,7 +4778,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) */ aio_context_acquire(new_context); bdrv_attach_aio_context(bs, new_context); - bdrv_parent_drained_end(bs); + bdrv_parent_drained_end(bs, NULL); aio_enable_external(ctx); aio_context_release(new_context); } diff --git a/block/io.c b/block/io.c index 6038a16c58..09de0a9070 100644 --- a/block/io.c +++ b/block/io.c @@ -40,22 +40,28 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags); -void bdrv_parent_drained_begin(BlockDriverState *bs) +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) { BdrvChild *c, *next; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { + if (c == ignore) { + continue; + } if (c->role->drained_begin) { c->role->drained_begin(c); } } } -void bdrv_parent_drained_end(BlockDriverState *bs) +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) { BdrvChild *c, *next; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { + if (c == ignore) { + continue; + } if (c->role->drained_end) { c->role->drained_end(c); } @@ -139,6 +145,7 @@ typedef struct { BlockDriverState *bs; bool done; bool begin; + BdrvChild *parent; } BdrvCoDrainData; static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) @@ -211,6 +218,9 @@ static bool bdrv_drain_recurse(BlockDriverState *bs) return waited; } +static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent); +static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent); + static void bdrv_co_drain_bh_cb(void *opaque) { BdrvCoDrainData *data = opaque; @@ -219,9 +229,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) bdrv_dec_in_flight(bs); if (data->begin) { - bdrv_drained_begin(bs); + bdrv_do_drained_begin(bs, data->parent); } else { - bdrv_drained_end(bs); + bdrv_do_drained_end(bs, data->parent); } data->done = true; @@ -229,7 +239,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) } static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, - bool begin) + bool begin, BdrvChild *parent) { BdrvCoDrainData data; @@ -243,6 +253,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, .bs = bs, .done = false, .begin = begin, + .parent = parent, }; bdrv_inc_in_flight(bs); aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), @@ -254,29 +265,34 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, assert(data.done); } -void bdrv_drained_begin(BlockDriverState *bs) +static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent) { if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, true); + bdrv_co_yield_to_drain(bs, true, parent); return; } /* Stop things in parent-to-child order */ if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { aio_disable_external(bdrv_get_aio_context(bs)); - bdrv_parent_drained_begin(bs); } + bdrv_parent_drained_begin(bs, parent); bdrv_drain_invoke(bs, true, false); bdrv_drain_recurse(bs); } -void bdrv_drained_end(BlockDriverState *bs) +void bdrv_drained_begin(BlockDriverState *bs) +{ + bdrv_do_drained_begin(bs, NULL); +} + +static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) { int old_quiesce_counter; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, false); + bdrv_co_yield_to_drain(bs, false, parent); return; } assert(bs->quiesce_counter > 0); @@ -284,12 +300,17 @@ void bdrv_drained_end(BlockDriverState *bs) /* Re-enable things in child-to-parent order */ bdrv_drain_invoke(bs, false, false); + bdrv_parent_drained_end(bs, parent); if (old_quiesce_counter == 1) { - bdrv_parent_drained_end(bs); aio_enable_external(bdrv_get_aio_context(bs)); } } +void bdrv_drained_end(BlockDriverState *bs) +{ + bdrv_do_drained_end(bs, NULL); +} + /* * Wait for pending requests to complete on a single BlockDriverState subtree, * and suspend block driver's internal I/O until next request arrives. @@ -346,7 +367,7 @@ void bdrv_drain_all_begin(void) /* Stop things in parent-to-child order */ aio_context_acquire(aio_context); aio_disable_external(aio_context); - bdrv_parent_drained_begin(bs); + bdrv_parent_drained_begin(bs, NULL); bdrv_drain_invoke(bs, true, true); aio_context_release(aio_context); @@ -391,7 +412,7 @@ void bdrv_drain_all_end(void) /* Re-enable things in child-to-parent order */ aio_context_acquire(aio_context); bdrv_drain_invoke(bs, false, true); - bdrv_parent_drained_end(bs); + bdrv_parent_drained_end(bs, NULL); aio_enable_external(aio_context); aio_context_release(aio_context); } diff --git a/include/block/block.h b/include/block/block.h index c05cac57e5..60c5d11029 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -585,7 +585,7 @@ void bdrv_io_unplug(BlockDriverState *bs); * Begin a quiesced section of all users of @bs. This is part of * bdrv_drained_begin. */ -void bdrv_parent_drained_begin(BlockDriverState *bs); +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); /** * bdrv_parent_drained_end: @@ -593,7 +593,7 @@ void bdrv_parent_drained_begin(BlockDriverState *bs); * End a quiesced section of all users of @bs. This is part of * bdrv_drained_end. */ -void bdrv_parent_drained_end(BlockDriverState *bs); +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); /** * bdrv_drained_begin: From b0165585900f050f403cecba9d89adeccf35dd6c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Dec 2017 17:05:44 +0100 Subject: [PATCH 28/35] block: Add bdrv_subtree_drained_begin/end() bdrv_drained_begin() waits for the completion of requests in the whole subtree, but it only actually keeps its immediate bs parameter quiesced until bdrv_drained_end(). Add a version that keeps the whole subtree drained. As of this commit, graph changes cannot be allowed during a subtree drained section, but this will be fixed soon. Signed-off-by: Kevin Wolf --- block/io.c | 54 ++++++++++++++++++++++++++++++++++--------- include/block/block.h | 13 +++++++++++ 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/block/io.c b/block/io.c index 09de0a9070..6befef166d 100644 --- a/block/io.c +++ b/block/io.c @@ -145,6 +145,7 @@ typedef struct { BlockDriverState *bs; bool done; bool begin; + bool recursive; BdrvChild *parent; } BdrvCoDrainData; @@ -218,8 +219,10 @@ static bool bdrv_drain_recurse(BlockDriverState *bs) return waited; } -static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent); -static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent); +static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + BdrvChild *parent); +static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + BdrvChild *parent); static void bdrv_co_drain_bh_cb(void *opaque) { @@ -229,9 +232,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) bdrv_dec_in_flight(bs); if (data->begin) { - bdrv_do_drained_begin(bs, data->parent); + bdrv_do_drained_begin(bs, data->recursive, data->parent); } else { - bdrv_do_drained_end(bs, data->parent); + bdrv_do_drained_end(bs, data->recursive, data->parent); } data->done = true; @@ -239,7 +242,8 @@ static void bdrv_co_drain_bh_cb(void *opaque) } static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, - bool begin, BdrvChild *parent) + bool begin, bool recursive, + BdrvChild *parent) { BdrvCoDrainData data; @@ -253,6 +257,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, .bs = bs, .done = false, .begin = begin, + .recursive = recursive, .parent = parent, }; bdrv_inc_in_flight(bs); @@ -265,10 +270,13 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, assert(data.done); } -static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent) +static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + BdrvChild *parent) { + BdrvChild *child, *next; + if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, true, parent); + bdrv_co_yield_to_drain(bs, true, recursive, parent); return; } @@ -280,19 +288,32 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent) bdrv_parent_drained_begin(bs, parent); bdrv_drain_invoke(bs, true, false); bdrv_drain_recurse(bs); + + if (recursive) { + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { + bdrv_do_drained_begin(child->bs, true, child); + } + } } void bdrv_drained_begin(BlockDriverState *bs) { - bdrv_do_drained_begin(bs, NULL); + bdrv_do_drained_begin(bs, false, NULL); } -static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) +void bdrv_subtree_drained_begin(BlockDriverState *bs) { + bdrv_do_drained_begin(bs, true, NULL); +} + +static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + BdrvChild *parent) +{ + BdrvChild *child, *next; int old_quiesce_counter; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, false, parent); + bdrv_co_yield_to_drain(bs, false, recursive, parent); return; } assert(bs->quiesce_counter > 0); @@ -304,11 +325,22 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) if (old_quiesce_counter == 1) { aio_enable_external(bdrv_get_aio_context(bs)); } + + if (recursive) { + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { + bdrv_do_drained_end(child->bs, true, child); + } + } } void bdrv_drained_end(BlockDriverState *bs) { - bdrv_do_drained_end(bs, NULL); + bdrv_do_drained_end(bs, false, NULL); +} + +void bdrv_subtree_drained_end(BlockDriverState *bs) +{ + bdrv_do_drained_end(bs, true, NULL); } /* diff --git a/include/block/block.h b/include/block/block.h index 60c5d11029..de9c5a2b9b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -607,6 +607,14 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); */ void bdrv_drained_begin(BlockDriverState *bs); +/** + * Like bdrv_drained_begin, but recursively begins a quiesced section for + * exclusive access to all child nodes as well. + * + * Graph changes are not allowed during a subtree drain section. + */ +void bdrv_subtree_drained_begin(BlockDriverState *bs); + /** * bdrv_drained_end: * @@ -614,6 +622,11 @@ void bdrv_drained_begin(BlockDriverState *bs); */ void bdrv_drained_end(BlockDriverState *bs); +/** + * End a quiescent section started by bdrv_subtree_drained_begin(). + */ +void bdrv_subtree_drained_end(BlockDriverState *bs); + void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, Error **errp); void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); From d2a85d0f42f328d5e4c948262234b4071e9a2a8e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Dec 2017 18:13:53 +0100 Subject: [PATCH 29/35] test-bdrv-drain: Tests for bdrv_subtree_drain Add a subtree drain version to the existing test cases. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 9098b77ab4..f363d51b34 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -85,6 +85,7 @@ static void aio_ret_cb(void *opaque, int ret) enum drain_type { BDRV_DRAIN_ALL, BDRV_DRAIN, + BDRV_SUBTREE_DRAIN, DRAIN_TYPE_MAX, }; @@ -93,6 +94,7 @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) switch (drain_type) { case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break; case BDRV_DRAIN: bdrv_drained_begin(bs); break; + case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break; default: g_assert_not_reached(); } } @@ -102,6 +104,7 @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) switch (drain_type) { case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break; case BDRV_DRAIN: bdrv_drained_end(bs); break; + case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break; default: g_assert_not_reached(); } } @@ -180,6 +183,11 @@ static void test_drv_cb_drain(void) test_drv_cb_common(BDRV_DRAIN, false); } +static void test_drv_cb_drain_subtree(void) +{ + test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); +} + static void test_quiesce_common(enum drain_type drain_type, bool recursive) { BlockBackend *blk; @@ -222,6 +230,11 @@ static void test_quiesce_drain(void) test_quiesce_common(BDRV_DRAIN, false); } +static void test_quiesce_drain_subtree(void) +{ + test_quiesce_common(BDRV_SUBTREE_DRAIN, true); +} + static void test_nested(void) { BlockBackend *blk; @@ -244,7 +257,8 @@ static void test_nested(void) /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */ int bs_quiesce = (outer != BDRV_DRAIN_ALL) + (inner != BDRV_DRAIN_ALL); - int backing_quiesce = 0; + int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) + + (inner == BDRV_SUBTREE_DRAIN); int backing_cb_cnt = (outer != BDRV_DRAIN) + (inner != BDRV_DRAIN); @@ -391,6 +405,11 @@ static void test_blockjob_drain(void) test_blockjob_common(BDRV_DRAIN); } +static void test_blockjob_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN); +} + int main(int argc, char **argv) { bdrv_init(); @@ -400,14 +419,20 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); + g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", + test_drv_cb_drain_subtree); g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); + g_test_add_func("/bdrv-drain/quiesce/drain_subtree", + test_quiesce_drain_subtree); g_test_add_func("/bdrv-drain/nested", test_nested); g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", + test_blockjob_drain_subtree); return g_test_run(); } From 0582eb1006518881ecf93ba69c051335334a03e6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 8 Dec 2017 18:51:16 +0100 Subject: [PATCH 30/35] test-bdrv-drain: Test behaviour in coroutine context If bdrv_do_drained_begin/end() are called in coroutine context, they first use a BH to get out of the coroutine context. Call some existing tests again from a coroutine to cover this code path. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index f363d51b34..354c6151a0 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -82,6 +82,34 @@ static void aio_ret_cb(void *opaque, int ret) *aio_ret = ret; } +typedef struct CallInCoroutineData { + void (*entry)(void); + bool done; +} CallInCoroutineData; + +static coroutine_fn void call_in_coroutine_entry(void *opaque) +{ + CallInCoroutineData *data = opaque; + + data->entry(); + data->done = true; +} + +static void call_in_coroutine(void (*entry)(void)) +{ + Coroutine *co; + CallInCoroutineData data = { + .entry = entry, + .done = false, + }; + + co = qemu_coroutine_create(call_in_coroutine_entry, &data); + qemu_coroutine_enter(co); + while (!data.done) { + aio_poll(qemu_get_aio_context(), true); + } +} + enum drain_type { BDRV_DRAIN_ALL, BDRV_DRAIN, @@ -188,6 +216,16 @@ static void test_drv_cb_drain_subtree(void) test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); } +static void test_drv_cb_co_drain(void) +{ + call_in_coroutine(test_drv_cb_drain); +} + +static void test_drv_cb_co_drain_subtree(void) +{ + call_in_coroutine(test_drv_cb_drain_subtree); +} + static void test_quiesce_common(enum drain_type drain_type, bool recursive) { BlockBackend *blk; @@ -235,6 +273,16 @@ static void test_quiesce_drain_subtree(void) test_quiesce_common(BDRV_SUBTREE_DRAIN, true); } +static void test_quiesce_co_drain(void) +{ + call_in_coroutine(test_quiesce_drain); +} + +static void test_quiesce_co_drain_subtree(void) +{ + call_in_coroutine(test_quiesce_drain_subtree); +} + static void test_nested(void) { BlockBackend *blk; @@ -422,11 +470,22 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", test_drv_cb_drain_subtree); + // XXX bdrv_drain_all() doesn't work in coroutine context + g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); + g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", + test_drv_cb_co_drain_subtree); + + g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); g_test_add_func("/bdrv-drain/quiesce/drain_subtree", test_quiesce_drain_subtree); + // XXX bdrv_drain_all() doesn't work in coroutine context + g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); + g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", + test_quiesce_co_drain_subtree); + g_test_add_func("/bdrv-drain/nested", test_nested); g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); From 27e64474a384e68ca928fa875930b921b53d61f4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 14 Dec 2017 11:41:17 +0100 Subject: [PATCH 31/35] test-bdrv-drain: Recursive draining with multiple parents Test that drain sections are correctly propagated through the graph. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 354c6151a0..690b585b4d 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -338,6 +338,79 @@ static void test_nested(void) blk_unref(blk); } +static void test_multiparent(void) +{ + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b, *backing; + BDRVTestState *a_s, *b_s, *backing_s; + + blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, + &error_abort); + a_s = bs_a->opaque; + blk_insert_bs(blk_a, bs_a, &error_abort); + + blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, + &error_abort); + b_s = bs_b->opaque; + blk_insert_bs(blk_b, bs_b, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs_a, backing, &error_abort); + bdrv_set_backing_hd(bs_b, backing, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(backing->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + g_assert_cmpint(backing_s->drain_count, ==, 1); + + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 2); + g_assert_cmpint(bs_b->quiesce_counter, ==, 2); + g_assert_cmpint(backing->quiesce_counter, ==, 2); + g_assert_cmpint(a_s->drain_count, ==, 2); + g_assert_cmpint(b_s->drain_count, ==, 2); + g_assert_cmpint(backing_s->drain_count, ==, 2); + + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(backing->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + g_assert_cmpint(backing_s->drain_count, ==, 1); + + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + bdrv_unref(backing); + bdrv_unref(bs_a); + bdrv_unref(bs_b); + blk_unref(blk_a); + blk_unref(blk_b); +} + typedef struct TestBlockJob { BlockJob common; @@ -487,6 +560,7 @@ int main(int argc, char **argv) test_quiesce_co_drain_subtree); g_test_add_func("/bdrv-drain/nested", test_nested); + g_test_add_func("/bdrv-drain/multiparent", test_multiparent); g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); From d736f119dae6d292e8d60f2e02fa51a79524113e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 18 Dec 2017 16:05:48 +0100 Subject: [PATCH 32/35] block: Allow graph changes in subtree drained section We need to remember how many of the drain sections in which a node is were recursive (i.e. subtree drain rather than node drain), so that they can be correctly applied when children are added or removed during the drained section. With this change, it is safe to modify the graph even inside a bdrv_subtree_drained_begin/end() section. Signed-off-by: Kevin Wolf --- block.c | 32 +++++++++++++++++++++++++++++--- block/io.c | 28 ++++++++++++++++++++++++---- include/block/block.h | 2 -- include/block/block_int.h | 5 +++++ 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/block.c b/block.c index 6c247167b8..8b46ba21b1 100644 --- a/block.c +++ b/block.c @@ -822,6 +822,18 @@ static void bdrv_child_cb_drained_end(BdrvChild *child) bdrv_drained_end(bs); } +static void bdrv_child_cb_attach(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + bdrv_apply_subtree_drain(child, bs); +} + +static void bdrv_child_cb_detach(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + bdrv_unapply_subtree_drain(child, bs); +} + static int bdrv_child_cb_inactivate(BdrvChild *child) { BlockDriverState *bs = child->opaque; @@ -889,6 +901,8 @@ const BdrvChildRole child_file = { .inherit_options = bdrv_inherited_options, .drained_begin = bdrv_child_cb_drained_begin, .drained_end = bdrv_child_cb_drained_end, + .attach = bdrv_child_cb_attach, + .detach = bdrv_child_cb_detach, .inactivate = bdrv_child_cb_inactivate, }; @@ -911,6 +925,8 @@ const BdrvChildRole child_format = { .inherit_options = bdrv_inherited_fmt_options, .drained_begin = bdrv_child_cb_drained_begin, .drained_end = bdrv_child_cb_drained_end, + .attach = bdrv_child_cb_attach, + .detach = bdrv_child_cb_detach, .inactivate = bdrv_child_cb_inactivate, }; @@ -953,6 +969,8 @@ static void bdrv_backing_attach(BdrvChild *c) parent->backing_blocker); bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET, parent->backing_blocker); + + bdrv_child_cb_attach(c); } static void bdrv_backing_detach(BdrvChild *c) @@ -963,6 +981,8 @@ static void bdrv_backing_detach(BdrvChild *c) bdrv_op_unblock_all(c->bs, parent->backing_blocker); error_free(parent->backing_blocker); parent->backing_blocker = NULL; + + bdrv_child_cb_detach(c); } /* @@ -1978,14 +1998,17 @@ static void bdrv_replace_child_noperm(BdrvChild *child, assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); } if (old_bs) { + /* Detach first so that the recursive drain sections coming from @child + * are already gone and we only end the drain sections that came from + * elsewhere. */ + if (child->role->detach) { + child->role->detach(child); + } if (old_bs->quiesce_counter && child->role->drained_end) { for (i = 0; i < old_bs->quiesce_counter; i++) { child->role->drained_end(child); } } - if (child->role->detach) { - child->role->detach(child); - } QLIST_REMOVE(child, next_parent); } @@ -1999,6 +2022,9 @@ static void bdrv_replace_child_noperm(BdrvChild *child, } } + /* Attach only after starting new drained sections, so that recursive + * drain sections coming from @child don't get an extra .drained_begin + * callback. */ if (child->role->attach) { child->role->attach(child); } diff --git a/block/io.c b/block/io.c index 6befef166d..7ea402352e 100644 --- a/block/io.c +++ b/block/io.c @@ -270,8 +270,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, assert(data.done); } -static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent) +void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + BdrvChild *parent) { BdrvChild *child, *next; @@ -290,6 +290,7 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, bdrv_drain_recurse(bs); if (recursive) { + bs->recursive_quiesce_counter++; QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_do_drained_begin(child->bs, true, child); } @@ -306,8 +307,8 @@ void bdrv_subtree_drained_begin(BlockDriverState *bs) bdrv_do_drained_begin(bs, true, NULL); } -static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, - BdrvChild *parent) +void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + BdrvChild *parent) { BdrvChild *child, *next; int old_quiesce_counter; @@ -327,6 +328,7 @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, } if (recursive) { + bs->recursive_quiesce_counter--; QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_do_drained_end(child->bs, true, child); } @@ -343,6 +345,24 @@ void bdrv_subtree_drained_end(BlockDriverState *bs) bdrv_do_drained_end(bs, true, NULL); } +void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) +{ + int i; + + for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { + bdrv_do_drained_begin(child->bs, true, child); + } +} + +void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) +{ + int i; + + for (i = 0; i < old_parent->recursive_quiesce_counter; i++) { + bdrv_do_drained_end(child->bs, true, child); + } +} + /* * Wait for pending requests to complete on a single BlockDriverState subtree, * and suspend block driver's internal I/O until next request arrives. diff --git a/include/block/block.h b/include/block/block.h index de9c5a2b9b..9b12774ddf 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -610,8 +610,6 @@ void bdrv_drained_begin(BlockDriverState *bs); /** * Like bdrv_drained_begin, but recursively begins a quiesced section for * exclusive access to all child nodes as well. - * - * Graph changes are not allowed during a subtree drain section. */ void bdrv_subtree_drained_begin(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index e107163594..29cafa4236 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -717,6 +717,8 @@ struct BlockDriverState { /* Accessed with atomic ops. */ int quiesce_counter; + int recursive_quiesce_counter; + unsigned int write_gen; /* Current data generation */ /* Protected by reqs_lock. */ @@ -768,6 +770,9 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); +void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); +void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); + int get_tmp_filename(char *filename, int size); BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, const char *filename); From acebcf8de82da9a583875a0d32cb6c9a9d29cdb5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 18 Dec 2017 12:59:34 +0100 Subject: [PATCH 33/35] test-bdrv-drain: Test graph changes in drained section Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 690b585b4d..d760e2b243 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -411,6 +411,85 @@ static void test_multiparent(void) blk_unref(blk_b); } +static void test_graph_change(void) +{ + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b, *backing; + BDRVTestState *a_s, *b_s, *backing_s; + + blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, + &error_abort); + a_s = bs_a->opaque; + blk_insert_bs(blk_a, bs_a, &error_abort); + + blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, + &error_abort); + b_s = bs_b->opaque; + blk_insert_bs(blk_b, bs_b, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs_a, backing, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); + + bdrv_set_backing_hd(bs_b, backing, &error_abort); + g_assert_cmpint(bs_a->quiesce_counter, ==, 5); + g_assert_cmpint(bs_b->quiesce_counter, ==, 5); + g_assert_cmpint(backing->quiesce_counter, ==, 5); + g_assert_cmpint(a_s->drain_count, ==, 5); + g_assert_cmpint(b_s->drain_count, ==, 5); + g_assert_cmpint(backing_s->drain_count, ==, 5); + + bdrv_set_backing_hd(bs_b, NULL, &error_abort); + g_assert_cmpint(bs_a->quiesce_counter, ==, 3); + g_assert_cmpint(bs_b->quiesce_counter, ==, 2); + g_assert_cmpint(backing->quiesce_counter, ==, 3); + g_assert_cmpint(a_s->drain_count, ==, 3); + g_assert_cmpint(b_s->drain_count, ==, 2); + g_assert_cmpint(backing_s->drain_count, ==, 3); + + bdrv_set_backing_hd(bs_b, backing, &error_abort); + g_assert_cmpint(bs_a->quiesce_counter, ==, 5); + g_assert_cmpint(bs_b->quiesce_counter, ==, 5); + g_assert_cmpint(backing->quiesce_counter, ==, 5); + g_assert_cmpint(a_s->drain_count, ==, 5); + g_assert_cmpint(b_s->drain_count, ==, 5); + g_assert_cmpint(backing_s->drain_count, ==, 5); + + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + bdrv_unref(backing); + bdrv_unref(bs_a); + bdrv_unref(bs_b); + blk_unref(blk_a); + blk_unref(blk_b); +} + typedef struct TestBlockJob { BlockJob common; @@ -561,6 +640,7 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/nested", test_nested); g_test_add_func("/bdrv-drain/multiparent", test_multiparent); + g_test_add_func("/bdrv-drain/graph-change", test_graph_change); g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); From 44487eb973f895d68989cf931e25f309ec9807f9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Dec 2017 13:53:36 +0100 Subject: [PATCH 34/35] commit: Simplify reopen of base Since commit bde70715, base is the only node that is reopened in commit_start(). This means that the code, which still involves an explicit BlockReopenQueue, can now be simplified by using bdrv_reopen(). Signed-off-by: Kevin Wolf Reviewed-by: Fam Zheng --- block/commit.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/block/commit.c b/block/commit.c index c5327551ce..bb6c904704 100644 --- a/block/commit.c +++ b/block/commit.c @@ -277,7 +277,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, const char *filter_node_name, Error **errp) { CommitBlockJob *s; - BlockReopenQueue *reopen_queue = NULL; int orig_base_flags; BlockDriverState *iter; BlockDriverState *commit_top_bs = NULL; @@ -299,12 +298,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, /* convert base to r/w, if necessary */ orig_base_flags = bdrv_get_flags(base); if (!(orig_base_flags & BDRV_O_RDWR)) { - reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL, - orig_base_flags | BDRV_O_RDWR); - } - - if (reopen_queue) { - bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err); + bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); goto fail; From 1a63a907507fbbcfaee3f622907ec244b7eabda8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Dec 2017 20:24:44 +0100 Subject: [PATCH 35/35] block: Keep nodes drained between reopen_queue/multiple The bdrv_reopen*() implementation doesn't like it if the graph is changed between queuing nodes for reopen and actually reopening them (one of the reasons is that queuing can be recursive). So instead of draining the device only in bdrv_reopen_multiple(), require that callers already drained all affected nodes, and assert this in bdrv_reopen_queue(). Signed-off-by: Kevin Wolf Reviewed-by: Fam Zheng --- block.c | 23 ++++++++++++++++------- block/replication.c | 6 ++++++ qemu-io-cmds.c | 3 +++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/block.c b/block.c index 8b46ba21b1..a8da4f2b25 100644 --- a/block.c +++ b/block.c @@ -2766,6 +2766,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, * returns a pointer to bs_queue, which is either the newly allocated * bs_queue, or the existing bs_queue being used. * + * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple(). */ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, @@ -2781,6 +2782,11 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BdrvChild *child; QDict *old_options, *explicit_options; + /* Make sure that the caller remembered to use a drained section. This is + * important to avoid graph changes between the recursive queuing here and + * bdrv_reopen_multiple(). */ + assert(bs->quiesce_counter > 0); + if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); QSIMPLEQ_INIT(bs_queue); @@ -2905,6 +2911,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, * If all devices prepare successfully, then the changes are committed * to all devices. * + * All affected nodes must be drained between bdrv_reopen_queue() and + * bdrv_reopen_multiple(). */ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp) { @@ -2914,11 +2922,8 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er assert(bs_queue != NULL); - aio_context_release(ctx); - bdrv_drain_all_begin(); - aio_context_acquire(ctx); - QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + assert(bs_entry->state.bs->quiesce_counter > 0); if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) { error_propagate(errp, local_err); goto cleanup; @@ -2947,8 +2952,6 @@ cleanup: } g_free(bs_queue); - bdrv_drain_all_end(); - return ret; } @@ -2958,12 +2961,18 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) { int ret = -1; Error *local_err = NULL; - BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); + BlockReopenQueue *queue; + bdrv_subtree_drained_begin(bs); + + queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); } + + bdrv_subtree_drained_end(bs); + return ret; } diff --git a/block/replication.c b/block/replication.c index e41e293d2b..b1ea3caa4b 100644 --- a/block/replication.c +++ b/block/replication.c @@ -394,6 +394,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, new_secondary_flags = s->orig_secondary_flags; } + bdrv_subtree_drained_begin(s->hidden_disk->bs); + bdrv_subtree_drained_begin(s->secondary_disk->bs); + if (orig_hidden_flags != new_hidden_flags) { reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL, new_hidden_flags); @@ -409,6 +412,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, reopen_queue, &local_err); error_propagate(errp, local_err); } + + bdrv_subtree_drained_end(s->hidden_disk->bs); + bdrv_subtree_drained_end(s->secondary_disk->bs); } static void backup_job_cleanup(BlockDriverState *bs) diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index de8e3de726..a6a70fc3dc 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -2013,8 +2013,11 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL; qemu_opts_reset(&reopen_opts); + bdrv_subtree_drained_begin(bs); brq = bdrv_reopen_queue(NULL, bs, opts, flags); bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err); + bdrv_subtree_drained_end(bs); + if (local_err) { error_report_err(local_err); } else {