Commit aa1361d5 authored by Kevin Wolf's avatar Kevin Wolf
Browse files

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: default avatarKevin Wolf <kwolf@redhat.com>
Reviewed-by: default avatarMax Reitz <mreitz@redhat.com>
parent ae23dde9
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -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();
+5 −0
Original line number Diff line number Diff line
@@ -89,6 +89,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
 */
+5 −0
Original line number Diff line number Diff line
@@ -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;
}