From patchwork Thu Oct 22 15:54:38 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 36698 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 23CC6B7BAE for ; Fri, 23 Oct 2009 03:05:12 +1100 (EST) Received: from localhost ([127.0.0.1]:55097 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N10A1-0006IN-88 for incoming@patchwork.ozlabs.org; Thu, 22 Oct 2009 12:05:09 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1N101B-0001Jr-1B for qemu-devel@nongnu.org; Thu, 22 Oct 2009 11:56:01 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1N1015-0001EQ-E5 for qemu-devel@nongnu.org; Thu, 22 Oct 2009 11:55:59 -0400 Received: from [199.232.76.173] (port=34379 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N1015-0001EM-2N for qemu-devel@nongnu.org; Thu, 22 Oct 2009 11:55:55 -0400 Received: from mx1.redhat.com ([209.132.183.28]:23456) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1N1014-00041y-G6 for qemu-devel@nongnu.org; Thu, 22 Oct 2009 11:55:54 -0400 Received: from int-mx04.intmail.prod.int.phx2.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.17]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id n9MFtrCI006840 for ; Thu, 22 Oct 2009 11:55:53 -0400 Received: from localhost.localdomain (dhcp-5-175.str.redhat.com [10.32.5.175]) by int-mx04.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id n9MFtlO0003324; Thu, 22 Oct 2009 11:55:52 -0400 From: Kevin Wolf To: qemu-devel@nongnu.org Date: Thu, 22 Oct 2009 17:54:38 +0200 Message-Id: <1256226882-26434-5-git-send-email-kwolf@redhat.com> In-Reply-To: <1256226882-26434-1-git-send-email-kwolf@redhat.com> References: <1256226882-26434-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.17 X-detected-operating-system: by monty-python.gnu.org: Genre and OS details not recognized. Cc: Kevin Wolf Subject: [Qemu-devel] [PATCH 4/8] Introduce contexts for asynchronous callbacks X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- async.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- qemu-common.h | 4 ++ qemu-tool.c | 13 +++++++ 3 files changed, 110 insertions(+), 7 deletions(-) diff --git a/async.c b/async.c index 60c1cb3..57ac3a8 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 4f42f95..f6c612a 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 ba24aa2..a1af4ac 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;