aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2009-10-22 17:54:38 +0200
committerAnthony Liguori <aliguori@us.ibm.com>2009-10-27 12:28:59 -0500
commit9a1e94812913667fe52d01c8ee2b7efe5f404478 (patch)
tree1bce5fd99aac008ef46b0c3fb7ccfb6988d094a4
parent4f999d05f55586ba73494c5ada6d9c5eb9eb800c (diff)
Introduce contexts for asynchronous callbacks
Add the possibility to use AIO and BHs without allowing foreign callbacks to be run. Basically, you put your own AIOs and BHs in a separate context. For details see the comments in the source. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
-rw-r--r--async.c100
-rw-r--r--qemu-common.h4
-rw-r--r--qemu-tool.c13
3 files changed, 110 insertions, 7 deletions
diff --git a/async.c b/async.c
index 60c1cb37da..57ac3a8180 100644
--- a/async.c
+++ b/async.c
@@ -23,6 +23,94 @@
*/
#include "qemu-common.h"
+#include "qemu-aio.h"
+
+/*
+ * An AsyncContext protects the callbacks of AIO requests and Bottom Halves
+ * against interfering with each other. A typical example is qcow2 that accepts
+ * asynchronous requests, but relies for manipulation of its metadata on
+ * synchronous bdrv_read/write that doesn't trigger any callbacks.
+ *
+ * However, these functions are often emulated using AIO which means that AIO
+ * callbacks must be run - but at the same time we must not run callbacks of
+ * other requests as they might start to modify metadata and corrupt the
+ * internal state of the caller of bdrv_read/write.
+ *
+ * To achieve the desired semantics we switch into a new AsyncContext.
+ * Callbacks must only be run if they belong to the current AsyncContext.
+ * Otherwise they need to be queued until their own context is active again.
+ * This is how you can make qemu_aio_wait() wait only for your own callbacks.
+ *
+ * The AsyncContexts form a stack. When you leave a AsyncContexts, you always
+ * return to the old ("parent") context.
+ */
+struct AsyncContext {
+ /* Consecutive number of the AsyncContext (position in the stack) */
+ int id;
+
+ /* Anchor of the list of Bottom Halves belonging to the context */
+ struct QEMUBH *first_bh;
+
+ /* Link to parent context */
+ struct AsyncContext *parent;
+};
+
+/* The currently active AsyncContext */
+static struct AsyncContext *async_context = &(struct AsyncContext) { 0 };
+
+/*
+ * Enter a new AsyncContext. Already scheduled Bottom Halves and AIO callbacks
+ * won't be called until this context is left again.
+ */
+void async_context_push(void)
+{
+ struct AsyncContext *new = qemu_mallocz(sizeof(*new));
+ new->parent = async_context;
+ new->id = async_context->id + 1;
+ async_context = new;
+}
+
+/* Run queued AIO completions and destroy Bottom Half */
+static void bh_run_aio_completions(void *opaque)
+{
+ QEMUBH **bh = opaque;
+ qemu_bh_delete(*bh);
+ qemu_free(bh);
+ qemu_aio_process_queue();
+}
+/*
+ * Leave the currently active AsyncContext. All Bottom Halves belonging to the
+ * old context are executed before changing the context.
+ */
+void async_context_pop(void)
+{
+ struct AsyncContext *old = async_context;
+ QEMUBH **bh;
+
+ /* Flush the bottom halves, we don't want to lose them */
+ while (qemu_bh_poll());
+
+ /* Switch back to the parent context */
+ async_context = async_context->parent;
+ qemu_free(old);
+
+ if (async_context == NULL) {
+ abort();
+ }
+
+ /* Schedule BH to run any queued AIO completions as soon as possible */
+ bh = qemu_malloc(sizeof(*bh));
+ *bh = qemu_bh_new(bh_run_aio_completions, bh);
+ qemu_bh_schedule(*bh);
+}
+
+/*
+ * Returns the ID of the currently active AsyncContext
+ */
+int get_async_context_id(void)
+{
+ return async_context->id;
+}
/***********************************************************/
/* bottom halves (can be seen as timers which expire ASAP) */
@@ -36,16 +124,14 @@ struct QEMUBH {
QEMUBH *next;
};
-static QEMUBH *first_bh = NULL;
-
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
{
QEMUBH *bh;
bh = qemu_mallocz(sizeof(QEMUBH));
bh->cb = cb;
bh->opaque = opaque;
- bh->next = first_bh;
- first_bh = bh;
+ bh->next = async_context->first_bh;
+ async_context->first_bh = bh;
return bh;
}
@@ -55,7 +141,7 @@ int qemu_bh_poll(void)
int ret;
ret = 0;
- for (bh = first_bh; bh; bh = bh->next) {
+ for (bh = async_context->first_bh; bh; bh = bh->next) {
if (!bh->deleted && bh->scheduled) {
bh->scheduled = 0;
if (!bh->idle)
@@ -66,7 +152,7 @@ int qemu_bh_poll(void)
}
/* remove deleted bhs */
- bhp = &first_bh;
+ bhp = &async_context->first_bh;
while (*bhp) {
bh = *bhp;
if (bh->deleted) {
@@ -112,7 +198,7 @@ void qemu_bh_update_timeout(int *timeout)
{
QEMUBH *bh;
- for (bh = first_bh; bh; bh = bh->next) {
+ for (bh = async_context->first_bh; bh; bh = bh->next) {
if (!bh->deleted && bh->scheduled) {
if (bh->idle) {
/* idle bottom halves will be polled at least
diff --git a/qemu-common.h b/qemu-common.h
index 3e73bf9523..b779cfe69d 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -93,6 +93,10 @@ typedef struct QEMUBH QEMUBH;
typedef void QEMUBHFunc(void *opaque);
+void async_context_push(void);
+void async_context_pop(void);
+int get_async_context_id(void);
+
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
void qemu_bh_schedule(QEMUBH *bh);
/* Bottom halfs that are scheduled from a bottom half handler are instantly
diff --git a/qemu-tool.c b/qemu-tool.c
index ba24aa2a13..a1af4acfef 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -43,6 +43,19 @@ void monitor_print_filename(Monitor *mon, const char *filename)
{
}
+void async_context_push(void)
+{
+}
+
+void async_context_pop(void)
+{
+}
+
+int get_async_context_id(void)
+{
+ return 0;
+}
+
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
{
QEMUBH *bh;