diff options
author | Kevin Wolf <kwolf@redhat.com> | 2018-08-17 18:54:18 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2018-09-25 15:50:15 +0200 |
commit | aa1361d54aac43094b98024b8b6c804eb6e41661 (patch) | |
tree | 5ae88b570aa3073f13e15c57e0594633b6d42584 | |
parent | ae23dde9dd486e57e152a0ebc9802caddedc45fc (diff) | |
download | qemu-aa1361d54aac43094b98024b8b6c804eb6e41661.zip qemu-aa1361d54aac43094b98024b8b6c804eb6e41661.tar.gz qemu-aa1361d54aac43094b98024b8b6c804eb6e41661.tar.bz2 |
block: Add missing locking in bdrv_co_drain_bh_cb()
bdrv_do_drained_begin/end() assume that they are called with the
AioContext lock of bs held. If we call drain functions from a coroutine
with the AioContext lock held, we yield and schedule a BH to move out of
coroutine context. This means that the lock for the home context of the
coroutine is released and must be re-acquired in the bottom half.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
-rw-r--r-- | block/io.c | 15 | ||||
-rw-r--r-- | include/qemu/coroutine.h | 5 | ||||
-rw-r--r-- | util/qemu-coroutine.c | 5 |
3 files changed, 25 insertions, 0 deletions
@@ -288,6 +288,18 @@ static void bdrv_co_drain_bh_cb(void *opaque) BlockDriverState *bs = data->bs; if (bs) { + AioContext *ctx = bdrv_get_aio_context(bs); + AioContext *co_ctx = qemu_coroutine_get_aio_context(co); + + /* + * When the coroutine yielded, the lock for its home context was + * released, so we need to re-acquire it here. If it explicitly + * acquired a different context, the lock is still held and we don't + * want to lock it a second time (or AIO_WAIT_WHILE() would hang). + */ + if (ctx == co_ctx) { + aio_context_acquire(ctx); + } bdrv_dec_in_flight(bs); if (data->begin) { bdrv_do_drained_begin(bs, data->recursive, data->parent, @@ -296,6 +308,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) bdrv_do_drained_end(bs, data->recursive, data->parent, data->ignore_bds_parents); } + if (ctx == co_ctx) { + aio_context_release(ctx); + } } else { assert(data->begin); bdrv_drain_all_begin(); diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index 6f8a487..9801e7f 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -90,6 +90,11 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co); void coroutine_fn qemu_coroutine_yield(void); /** + * Get the AioContext of the given coroutine + */ +AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co); + +/** * Get the currently executing coroutine */ Coroutine *coroutine_fn qemu_coroutine_self(void); diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 1ba4191..2295928 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -198,3 +198,8 @@ bool qemu_coroutine_entered(Coroutine *co) { return co->caller; } + +AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co) +{ + return co->ctx; +} |