[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-block] [PATCH 17/20] block: only call aio_poll on the current thre
From: |
Paolo Bonzini |
Subject: |
[Qemu-block] [PATCH 17/20] block: only call aio_poll on the current thread's AioContext |
Date: |
Mon, 17 Oct 2016 15:54:27 +0200 |
aio_poll is not thread safe; for example bdrv_drain can hang if
the last in-flight I/O operation is completed in the I/O thread after
the main thread has checked bs->in_flight.
The bug remains latent as long as all of it is called within
aio_context_acquire/aio_context_release, but this will change soon.
To fix this, if bdrv_drain is called from outside the I/O thread,
signal the main AioContext through a dummy bottom half. The event
loop then only runs in the I/O thread.
Reviewed-by: Fam Zheng <address@hidden>
Signed-off-by: Paolo Bonzini <address@hidden>
---
v1->v2: bdrv_wakeup moved here and documented [Stefan]
async.c | 1 +
block.c | 2 ++
block/io.c | 12 ++++++++++++
block/nfs.c | 1 +
block/sheepdog.c | 3 +++
hw/scsi/virtio-scsi-dataplane.c | 4 +---
include/block/block.h | 24 +++++++++++++++++++++---
include/block/block_int.h | 17 +++++++++++++++++
8 files changed, 58 insertions(+), 6 deletions(-)
diff --git a/async.c b/async.c
index f30d011..fb37b03 100644
--- a/async.c
+++ b/async.c
@@ -61,6 +61,7 @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb,
void *opaque)
smp_wmb();
ctx->first_bh = bh;
qemu_mutex_unlock(&ctx->bh_lock);
+ aio_notify(ctx);
}
QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
diff --git a/block.c b/block.c
index fbe485c..a17baab 100644
--- a/block.c
+++ b/block.c
@@ -2090,7 +2090,9 @@ int bdrv_reopen_multiple(AioContext *ctx,
BlockReopenQueue *bs_queue, Error **er
assert(bs_queue != NULL);
+ aio_context_release(ctx);
bdrv_drain_all();
+ aio_context_acquire(ctx);
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
diff --git a/block/io.c b/block/io.c
index f0682dd..91f8a08 100644
--- a/block/io.c
+++ b/block/io.c
@@ -474,9 +474,21 @@ void bdrv_inc_in_flight(BlockDriverState *bs)
atomic_inc(&bs->in_flight);
}
+static void dummy_bh_cb(void *opaque)
+{
+}
+
+void bdrv_wakeup(BlockDriverState *bs)
+{
+ if (bs->wakeup) {
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
+ }
+}
+
void bdrv_dec_in_flight(BlockDriverState *bs)
{
atomic_dec(&bs->in_flight);
+ bdrv_wakeup(bs);
}
static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
diff --git a/block/nfs.c b/block/nfs.c
index 7474fbc..88c60a9 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -505,6 +505,7 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context
*nfs, void *data,
error_report("NFS Error: %s", nfs_get_error(nfs));
}
task->complete = 1;
+ bdrv_wakeup(task->bs);
}
static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 16a5c1c..1fb9173 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -702,6 +702,9 @@ out:
srco->ret = ret;
srco->finished = true;
+ if (srco->bs) {
+ bdrv_wakeup(srco->bs);
+ }
}
/*
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index b173b94..9424f0e 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -189,13 +189,11 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
assert(s->ctx == iothread_get_aio_context(vs->conf.iothread));
aio_context_acquire(s->ctx);
-
virtio_scsi_clear_aio(s);
+ aio_context_release(s->ctx);
blk_drain_all(); /* ensure there are no in-flight requests */
- aio_context_release(s->ctx);
-
for (i = 0; i < vs->conf.num_queues + 2; i++) {
virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
}
diff --git a/include/block/block.h b/include/block/block.h
index fb86611..df25665 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -343,9 +343,27 @@ void bdrv_drain_all(void);
#define BDRV_POLL_WHILE(bs, cond) ({ \
bool waited_ = false; \
BlockDriverState *bs_ = (bs); \
- while ((cond)) { \
- aio_poll(bdrv_get_aio_context(bs_), true); \
- waited_ = true; \
+ AioContext *ctx_ = bdrv_get_aio_context(bs_); \
+ if (aio_context_in_iothread(ctx_)) { \
+ while ((cond)) { \
+ aio_poll(ctx_, true); \
+ waited_ = true; \
+ } \
+ } else { \
+ assert(qemu_get_current_aio_context() == \
+ qemu_get_aio_context()); \
+ /* Ask bdrv_dec_in_flight to wake up the main \
+ * QEMU AioContext. \
+ */ \
+ assert(!bs_->wakeup); \
+ bs_->wakeup = true; \
+ while ((cond)) { \
+ aio_context_release(ctx_); \
+ aio_poll(qemu_get_aio_context(), true); \
+ aio_context_acquire(ctx_); \
+ waited_ = true; \
+ } \
+ bs_->wakeup = false; \
} \
waited_; })
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 5a7308b..2289ca1 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -473,6 +473,8 @@ struct BlockDriverState {
unsigned int in_flight;
unsigned int serialising_in_flight;
+ bool wakeup;
+
/* Offset after the highest byte written to */
uint64_t wr_highest_offset;
@@ -631,6 +633,21 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
void (*aio_context_detached)(void *),
void *opaque);
+/**
+ * bdrv_wakeup:
+ * @bs: The BlockDriverState for which an I/O operation has been completed.
+ *
+ * Wake up the main thread if it is waiting on BDRV_POLL_WHILE. During
+ * synchronous I/O on a BlockDriverState that is attached to another
+ * I/O thread, the main thread lets the I/O thread's event loop run,
+ * waiting for the I/O operation to complete. A bdrv_wakeup will wake
+ * up the main thread if necessary.
+ *
+ * Manual calls to bdrv_wakeup are rarely necessary, because
+ * bdrv_dec_in_flight already calls it.
+ */
+void bdrv_wakeup(BlockDriverState *bs);
+
#ifdef _WIN32
int is_windows_drive(const char *filename);
#endif
--
2.7.4
- [Qemu-block] [PATCH 08/20] nfs: move nfs_set_events out of the while loops, (continued)
- [Qemu-block] [PATCH 08/20] nfs: move nfs_set_events out of the while loops, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 11/20] aio: introduce qemu_get_current_aio_context, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 10/20] sheepdog: use BDRV_POLL_WHILE, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 12/20] iothread: detach all block devices before stopping them, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 13/20] replication: pass BlockDriverState to reopen_backing_file, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 14/20] block: prepare bdrv_reopen_multiple to release AioContext, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 15/20] qemu-io: acquire AioContext, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 16/20] qemu-img: call aio_context_acquire/release around block job, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 17/20] block: only call aio_poll on the current thread's AioContext,
Paolo Bonzini <=
- [Qemu-block] [PATCH 19/20] qemu-thread: introduce QemuRecMutex, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 18/20] iothread: release AioContext around aio_poll, Paolo Bonzini, 2016/10/17
- [Qemu-block] [PATCH 20/20] aio: convert from RFifoLock to QemuRecMutex, Paolo Bonzini, 2016/10/17
- Re: [Qemu-block] [PATCH v2 00/20] dataplane: remove RFifoLock, Fam Zheng, 2016/10/18