From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39573) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bCVJZ-0001uu-0T for qemu-devel@nongnu.org; Mon, 13 Jun 2016 13:06:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bCVJS-000214-V7 for qemu-devel@nongnu.org; Mon, 13 Jun 2016 13:06:15 -0400 Received: from mx1.redhat.com ([209.132.183.28]:48522) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bCVJS-00020z-Mt for qemu-devel@nongnu.org; Mon, 13 Jun 2016 13:06:10 -0400 From: Stefan Hajnoczi Date: Mon, 13 Jun 2016 18:05:34 +0100 Message-Id: <1465837535-30067-15-git-send-email-stefanha@redhat.com> In-Reply-To: <1465837535-30067-1-git-send-email-stefanha@redhat.com> References: <1465837535-30067-1-git-send-email-stefanha@redhat.com> Subject: [Qemu-devel] [PATCH v3 14/15] blockjob: add AioContext attach/detach callbacks List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Kevin Wolf , jjherne@linux.vnet.ibm.com, Fam Zheng , Paolo Bonzini , Jeff Cody , mreitz@redhat.com, Stefan Hajnoczi Block jobs need callbacks to get their affairs in order when the AioContext is switched. Simple block jobs can get away without implementing these callbacks. The callbacks are needed if the block job accesses other BlockDriverStates. Other BDSes need to be moved to the new AioContext in the attach callback. The detach callback must be used to quiesce asynchronous I/O. Although bdrv_set_aio_context() internally calls bdrv_drain(), this isn't enough when multiple BDSes are accessed by the job: When completing requests on one BDS submits new requests on another BDS, especially if this is cyclical, then a custom detach callback is needed. Signed-off-by: Stefan Hajnoczi --- blockjob.c | 33 +++++++++++++++++++++++++++++++++ include/block/blockjob.h | 14 ++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/blockjob.c b/blockjob.c index b810d73..dd384fe 100644 --- a/blockjob.c +++ b/blockjob.c @@ -60,6 +60,33 @@ BlockJob *block_job_next(BlockJob *job) return QLIST_NEXT(job, job_list); } +static void block_job_attached_aio_context(AioContext *new_context, + void *opaque) +{ + BlockJob *job = opaque; + + if (job->driver->attached_aio_context) { + job->driver->attached_aio_context(job, new_context); + } + + block_job_resume(job); +} + +static void block_job_detach_aio_context(void *opaque) +{ + BlockJob *job = opaque; + + block_job_pause(job); + + if (job->driver->detach_aio_context) { + job->driver->detach_aio_context(job); + } + + while (job->busy) { + aio_poll(blk_get_aio_context(job->blk), true); + } +} + void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, int64_t speed, BlockCompletionFunc *cb, void *opaque, Error **errp) @@ -92,6 +119,9 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, QLIST_INSERT_HEAD(&block_jobs, job, job_list); + blk_add_aio_context_notifier(blk, block_job_attached_aio_context, + block_job_detach_aio_context, job); + /* Only set speed when necessary to avoid NotSupported error */ if (speed != 0) { Error *local_err = NULL; @@ -117,6 +147,9 @@ void block_job_unref(BlockJob *job) BlockDriverState *bs = blk_bs(job->blk); bs->job = NULL; bdrv_op_unblock_all(bs, job->blocker); + blk_remove_aio_context_notifier(job->blk, + block_job_attached_aio_context, + block_job_detach_aio_context, job); blk_unref(job->blk); error_free(job->blocker); g_free(job->id); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index f83a4f0..604aff8 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -70,6 +70,20 @@ typedef struct BlockJobDriver { * never both. */ void (*abort)(BlockJob *job); + + /** + * If the callback is not NULL, it will be invoked before the job is + * resumed in a new AioContext. This is the place to move any resources + * besides job->blk to the new AioContext. + */ + void (*attached_aio_context)(BlockJob *job, AioContext *new_context); + + /** + * If the callback is not NULL, it will be invoked after the job is paused + * but before job->blk is detached from the old AioContext. This is the + * place to complete all asynchronous I/O that is in flight. + */ + void (*detach_aio_context)(BlockJob *job); } BlockJobDriver; /** -- 2.5.5