diff mbox

[v3,14/15] blockjob: add AioContext attach/detach callbacks

Message ID 1465837535-30067-15-git-send-email-stefanha@redhat.com
State New
Headers show

Commit Message

Stefan Hajnoczi June 13, 2016, 5:05 p.m. UTC
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 <stefanha@redhat.com>
---
 blockjob.c               | 33 +++++++++++++++++++++++++++++++++
 include/block/blockjob.h | 14 ++++++++++++++
 2 files changed, 47 insertions(+)
diff mbox

Patch

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;
 
 /**