diff options
-rw-r--r-- | block/io.c | 41 |
1 files changed, 24 insertions, 17 deletions
diff --git a/block/io.c b/block/io.c index 6343d85476..24205f5168 100644 --- a/block/io.c +++ b/block/io.c @@ -312,17 +312,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) 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); - } + aio_context_acquire(ctx); bdrv_dec_in_flight(bs); if (data->begin) { assert(!data->drained_end_counter); @@ -334,9 +324,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) data->ignore_bds_parents, data->drained_end_counter); } - if (ctx == co_ctx) { - aio_context_release(ctx); - } + aio_context_release(ctx); } else { assert(data->begin); bdrv_drain_all_begin(); @@ -354,13 +342,16 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, int *drained_end_counter) { BdrvCoDrainData data; + Coroutine *self = qemu_coroutine_self(); + AioContext *ctx = bdrv_get_aio_context(bs); + AioContext *co_ctx = qemu_coroutine_get_aio_context(self); /* Calling bdrv_drain() from a BH ensures the current coroutine yields and * other coroutines run if they were queued by aio_co_enter(). */ assert(qemu_in_coroutine()); data = (BdrvCoDrainData) { - .co = qemu_coroutine_self(), + .co = self, .bs = bs, .done = false, .begin = begin, @@ -374,13 +365,29 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, if (bs) { bdrv_inc_in_flight(bs); } - replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs), - bdrv_co_drain_bh_cb, &data); + + /* + * Temporarily drop the lock across yield or we would get deadlocks. + * bdrv_co_drain_bh_cb() reaquires the lock as needed. + * + * When we yield below, the lock for the current context will be + * released, so if this is actually the lock that protects bs, don't drop + * it a second time. + */ + if (ctx != co_ctx) { + aio_context_release(ctx); + } + replay_bh_schedule_oneshot_event(ctx, bdrv_co_drain_bh_cb, &data); qemu_coroutine_yield(); /* If we are resumed from some other event (such as an aio completion or a * timer callback), it is a bug in the caller that should be fixed. */ assert(data.done); + + /* Reaquire the AioContext of bs if we dropped it */ + if (ctx != co_ctx) { + aio_context_acquire(ctx); + } } void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, |