diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2016-10-27 12:49:05 +0200 |
---|---|---|
committer | Fam Zheng <famz@redhat.com> | 2016-10-28 21:50:18 +0800 |
commit | c9d1a56174339b0afdef63b7d151b38f4bb6dae5 (patch) | |
tree | 1eb8359be422f1abbf98c5781cee063163861f67 /include | |
parent | 9e944cb4744b527dd93aa989023739375a8880fb (diff) |
block: only call aio_poll on the current thread's AioContext
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 <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1477565348-5458-18-git-send-email-pbonzini@redhat.com>
Signed-off-by: Fam Zheng <famz@redhat.com>
Diffstat (limited to 'include')
-rw-r--r-- | include/block/block.h | 26 | ||||
-rw-r--r-- | include/block/block_int.h | 17 |
2 files changed, 40 insertions, 3 deletions
diff --git a/include/block/block.h b/include/block/block.h index 84257ab940..b7dc7d54ae 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -337,9 +337,29 @@ 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. Extra I/O threads never take \ + * other I/O threads' AioContexts (see for example \ + * block_job_defer_to_main_loop for how to do it). \ + */ \ + 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 1044dfef43..e7ff58419c 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -475,6 +475,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; @@ -633,6 +635,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 |