block: Add missing locking in bdrv_co_drain_bh_cb()
authorKevin Wolf <kwolf@redhat.com>
Fri, 17 Aug 2018 16:54:18 +0000 (18:54 +0200)
committerKevin Wolf <kwolf@redhat.com>
Tue, 25 Sep 2018 13:50:15 +0000 (15:50 +0200)
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>
block/io.c
include/qemu/coroutine.h
util/qemu-coroutine.c

index 7100344c7b3575dec609f6de8a12d549142b9a8a..914ba78f1a3f1984607196c665cbb873c7ea0593 100644 (file)
@@ -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();
index 6f8a487041fef2a7cf57204e1e82d1dc09743da2..9801e7f5a4978bf4a5a74d33aeb6f00cf0f5536b 100644 (file)
@@ -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
  */
index 1ba4191b844718712c274b0acfb95e340ac0258f..2295928d3371c599d0fa1d9633245f466da89817 100644 (file)
@@ -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;
+}