From: Stefan Hajnoczi <stefanha@redhat.com>
To: qemu-devel@nongnu.org
Cc: Anthony Liguori <aliguori@us.ibm.com>
Subject: [Qemu-devel] [PATCH 24/28] coroutine: use AioContext for CoQueue BH
Date: Fri, 15 Mar 2013 16:14:21 +0100 [thread overview]
Message-ID: <1363360465-5247-25-git-send-email-stefanha@redhat.com> (raw)
In-Reply-To: <1363360465-5247-1-git-send-email-stefanha@redhat.com>
CoQueue uses a BH to awake coroutines that were made ready to run again
using qemu_co_queue_next() or qemu_co_queue_restart_all(). The BH
currently runs in the iothread AioContext and would break coroutines
that run in a different AioContext.
This is a slightly tricky problem because the lifetime of the BH exceeds
that of the CoQueue. This means coroutines can be awoken after CoQueue
itself has been freed. Also, there is no qemu_co_queue_destroy()
function which we could use to handle freeing resources.
Introducing qemu_co_queue_destroy() has a ripple effect of requiring us
to also add qemu_co_mutex_destroy() and qemu_co_rwlock_destroy(), as
well as updating all callers. Avoid doing that.
We also cannot switch from BH to GIdle function because aio_poll() does
not dispatch GIdle functions. (GIdle functions make memory management
slightly easier because they free themselves.)
Finally, I don't want to move unlock_queue and unlock_bh into
AioContext. That would break encapsulation - AioContext isn't supposed
to know about CoQueue.
This patch implements a different solution: each qemu_co_queue_next() or
qemu_co_queue_restart_all() call creates a new BH and list of coroutines
to wake up. Callers tend to invoke qemu_co_queue_next() and
qemu_co_queue_restart_all() occasionally after blocking I/O, so creating
a new BH for each call shouldn't be massively inefficient.
Note that this patch does not add an interface for specifying the
AioContext. That is left to future patches which will convert CoQueue,
CoMutex, and CoRwlock to expose AioContext.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/block/coroutine.h | 1 +
qemu-coroutine-lock.c | 55 ++++++++++++++++++++++++++++++++---------------
2 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/include/block/coroutine.h b/include/block/coroutine.h
index c31fae3..a978162 100644
--- a/include/block/coroutine.h
+++ b/include/block/coroutine.h
@@ -104,6 +104,7 @@ bool qemu_in_coroutine(void);
*/
typedef struct CoQueue {
QTAILQ_HEAD(, Coroutine) entries;
+ AioContext *ctx;
} CoQueue;
/**
diff --git a/qemu-coroutine-lock.c b/qemu-coroutine-lock.c
index 97ef01c..86efe1f 100644
--- a/qemu-coroutine-lock.c
+++ b/qemu-coroutine-lock.c
@@ -29,28 +29,36 @@
#include "block/aio.h"
#include "trace.h"
-static QTAILQ_HEAD(, Coroutine) unlock_bh_queue =
- QTAILQ_HEAD_INITIALIZER(unlock_bh_queue);
-static QEMUBH* unlock_bh;
+/* Coroutines are awoken from a BH to allow the current coroutine to complete
+ * its flow of execution. The BH may run after the CoQueue has been destroyed,
+ * so keep BH data in a separate heap-allocated struct.
+ */
+typedef struct {
+ QEMUBH *bh;
+ QTAILQ_HEAD(, Coroutine) entries;
+} CoQueueNextData;
static void qemu_co_queue_next_bh(void *opaque)
{
+ CoQueueNextData *data = opaque;
Coroutine *next;
trace_qemu_co_queue_next_bh();
- while ((next = QTAILQ_FIRST(&unlock_bh_queue))) {
- QTAILQ_REMOVE(&unlock_bh_queue, next, co_queue_next);
+ while ((next = QTAILQ_FIRST(&data->entries))) {
+ QTAILQ_REMOVE(&data->entries, next, co_queue_next);
qemu_coroutine_enter(next, NULL);
}
+
+ qemu_bh_delete(data->bh);
+ g_slice_free(CoQueueNextData, data);
}
void qemu_co_queue_init(CoQueue *queue)
{
QTAILQ_INIT(&queue->entries);
- if (!unlock_bh) {
- unlock_bh = qemu_bh_new(qemu_co_queue_next_bh, NULL);
- }
+ /* This will be exposed to callers once there are multiple AioContexts */
+ queue->ctx = qemu_get_aio_context();
}
void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
@@ -69,26 +77,39 @@ void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue)
assert(qemu_in_coroutine());
}
-bool qemu_co_queue_next(CoQueue *queue)
+static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
{
Coroutine *next;
+ CoQueueNextData *data;
+
+ if (QTAILQ_EMPTY(&queue->entries)) {
+ return false;
+ }
- next = QTAILQ_FIRST(&queue->entries);
- if (next) {
+ data = g_slice_new(CoQueueNextData);
+ data->bh = aio_bh_new(queue->ctx, qemu_co_queue_next_bh, data);
+ QTAILQ_INIT(&data->entries);
+ qemu_bh_schedule(data->bh);
+
+ while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) {
QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
- QTAILQ_INSERT_TAIL(&unlock_bh_queue, next, co_queue_next);
+ QTAILQ_INSERT_TAIL(&data->entries, next, co_queue_next);
trace_qemu_co_queue_next(next);
- qemu_bh_schedule(unlock_bh);
+ if (single) {
+ break;
+ }
}
+ return true;
+}
- return (next != NULL);
+bool qemu_co_queue_next(CoQueue *queue)
+{
+ return qemu_co_queue_do_restart(queue, true);
}
void qemu_co_queue_restart_all(CoQueue *queue)
{
- while (qemu_co_queue_next(queue)) {
- /* Do nothing */
- }
+ qemu_co_queue_do_restart(queue, false);
}
bool qemu_co_queue_empty(CoQueue *queue)
--
1.8.1.4
next prev parent reply other threads:[~2013-03-15 15:15 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-03-15 15:13 [Qemu-devel] [PULL 00/28] Block patches Stefan Hajnoczi
2013-03-15 15:13 ` [Qemu-devel] [PATCH 01/28] block: Add options QDict to .bdrv_open() Stefan Hajnoczi
2013-03-15 15:13 ` [Qemu-devel] [PATCH 02/28] block: Add options QDict to bdrv_open() prototype Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 03/28] Add qdict_clone_shallow() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 04/28] block: Add options QDict to bdrv_open_common() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 05/28] qemu-option: Add qemu_opts_absorb_qdict() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 06/28] blockdev: Keep a copy of DriveInfo.serial Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 07/28] block: Support driver specific options in drive_init() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 08/28] qcow2: Allow lazy refcounts to be enabled on the command line Stefan Hajnoczi
2013-03-15 17:02 ` Paolo Bonzini
2013-03-15 17:35 ` Anthony Liguori
2013-03-15 15:14 ` [Qemu-devel] [PATCH 09/28] qcow2: flush refcount cache correctly in alloc_refcount_block() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 10/28] qcow2: flush refcount cache correctly in qcow2_write_snapshots() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 11/28] qcow2: set L2 cache dependency in qcow2_alloc_bytes() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 12/28] qcow2: flush in qcow2_update_snapshot_refcount() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 13/28] qcow2: drop flush in update_cluster_refcount() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 14/28] qcow2: drop unnecessary flush in qcow2_update_snapshot_refcount() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 15/28] qcow2: make is_allocated return true for zero clusters Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 16/28] sheepdog: use non-blocking fd in coroutine context Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 17/28] sheepdog: set io_flush handler in do_co_req Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 18/28] main-loop: add qemu_get_aio_context() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 19/28] threadpool: move globals into struct ThreadPool Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 20/28] threadpool: add thread_pool_new() and thread_pool_free() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 21/28] aio: add a ThreadPool instance to AioContext Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 22/28] block: add bdrv_get_aio_context() Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 23/28] threadpool: drop global thread pool Stefan Hajnoczi
2013-03-15 15:14 ` Stefan Hajnoczi [this message]
2013-03-15 15:14 ` [Qemu-devel] [PATCH 25/28] dataplane: fix hang introduced by AioContext transition Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 26/28] qemu-iotests: add tests for rebasing zero clusters Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 27/28] qemu-iotests: use -nographic in test case 007 Stefan Hajnoczi
2013-03-15 15:14 ` [Qemu-devel] [PATCH 28/28] blockdev: Fix up copyright and permission notice Stefan Hajnoczi
2013-03-15 15:46 ` [Qemu-devel] [PULL 00/28] Block patches Anthony Liguori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1363360465-5247-25-git-send-email-stefanha@redhat.com \
--to=stefanha@redhat.com \
--cc=aliguori@us.ibm.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).