[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v2 08/16] blockjob: introduce .drain callback for jo
From: |
Paolo Bonzini |
Subject: |
[Qemu-devel] [PATCH v2 08/16] blockjob: introduce .drain callback for jobs |
Date: |
Wed, 16 Mar 2016 15:16:49 +0100 |
This is required to decouple block jobs from running in an
AioContext. With multiqueue block devices, a BlockDriverState
does not really belong to a single AioContext.
The solution is to first wait until all I/O operations are
complete; then loop in the main thread for the block job to
complete entirely.
Reviewed-by: Fam Zheng <address@hidden>
Signed-off-by: Paolo Bonzini <address@hidden>
---
block/backup.c | 7 +++++++
block/mirror.c | 12 ++++++++++--
blockjob.c | 16 +++++++++++++---
include/block/blockjob.h | 7 +++++++
4 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/block/backup.c b/block/backup.c
index ab3e345..eea337e 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -257,6 +257,12 @@ static void backup_abort(BlockJob *job)
}
}
+static void backup_drain(BlockJob *job)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+ bdrv_drain(s->target);
+}
+
static const BlockJobDriver backup_job_driver = {
.instance_size = sizeof(BackupBlockJob),
.job_type = BLOCK_JOB_TYPE_BACKUP,
@@ -264,6 +270,7 @@ static const BlockJobDriver backup_job_driver = {
.iostatus_reset = backup_iostatus_reset,
.commit = backup_commit,
.abort = backup_abort,
+ .drain = backup_drain,
};
static BlockErrorAction backup_error_action(BackupBlockJob *job,
diff --git a/block/mirror.c b/block/mirror.c
index 1aecafd..b052dbf 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -433,7 +433,7 @@ static void mirror_free_init(MirrorBlockJob *s)
}
}
-static void mirror_drain(MirrorBlockJob *s)
+static void mirror_wait_for_completion(MirrorBlockJob *s)
{
while (s->in_flight > 0) {
mirror_wait_for_io(s);
@@ -699,7 +699,7 @@ immediate_exit:
* the target is a copy of the source.
*/
assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
- mirror_drain(s);
+ mirror_wait_for_completion(s);
}
assert(s->in_flight == 0);
@@ -780,12 +780,19 @@ static void mirror_complete(BlockJob *job, Error **errp)
block_job_enter(&s->common);
}
+static void mirror_drain(BlockJob *job)
+{
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+ bdrv_drain(s->target);
+}
+
static const BlockJobDriver mirror_job_driver = {
.instance_size = sizeof(MirrorBlockJob),
.job_type = BLOCK_JOB_TYPE_MIRROR,
.set_speed = mirror_set_speed,
.iostatus_reset= mirror_iostatus_reset,
.complete = mirror_complete,
+ .drain = mirror_drain,
};
static const BlockJobDriver commit_active_job_driver = {
@@ -795,6 +802,7 @@ static const BlockJobDriver commit_active_job_driver = {
.iostatus_reset
= mirror_iostatus_reset,
.complete = mirror_complete,
+ .drain = mirror_drain,
};
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
diff --git a/blockjob.c b/blockjob.c
index 9fc37ca..5bb6f9b 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -289,16 +289,26 @@ static int block_job_finish_sync(BlockJob *job,
assert(bs->job == job);
block_job_ref(job);
+
+ /* finish will call block_job_enter (see e.g. block_job_cancel,
+ * or mirror_complete in block/mirror.c). Barring bugs in the
+ * job coroutine, bdrv_drain should be enough to induce progress
+ * until the job completes or moves to the main thread.
+ */
finish(job, &local_err);
if (local_err) {
error_propagate(errp, local_err);
block_job_unref(job);
return -EBUSY;
}
+ while (!job->deferred_to_main_loop && !job->completed) {
+ bdrv_drain(bs);
+ if (job->driver->drain) {
+ job->driver->drain(job);
+ }
+ }
while (!job->completed) {
- aio_poll(job->deferred_to_main_loop ? qemu_get_aio_context() :
- bdrv_get_aio_context(bs),
- true);
+ aio_poll(qemu_get_aio_context(), true);
}
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
block_job_unref(job);
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 8bedc49..8e564df 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -70,6 +70,13 @@ typedef struct BlockJobDriver {
* never both.
*/
void (*abort)(BlockJob *job);
+
+ /**
+ * If the callback is not NULL, it will be invoked when the job has to be
+ * synchronously cancelled or completed; it should drain BlockDriverStates
+ * as required to ensure progress.
+ */
+ void (*drain)(BlockJob *job);
} BlockJobDriver;
/**
--
1.8.3.1
- [Qemu-devel] [PATCH v2 05/16] mirror: use bottom half to re-enter coroutine, (continued)
- [Qemu-devel] [PATCH v2 05/16] mirror: use bottom half to re-enter coroutine, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 07/16] block: change drain to look only at one child at a time, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 09/16] block: wait for all pending I/O when doing synchronous requests, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 12/16] aio: introduce aio_context_in_iothread, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 11/16] sheepdog: disable dataplane, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 08/16] blockjob: introduce .drain callback for jobs,
Paolo Bonzini <=
- [Qemu-devel] [PATCH v2 10/16] nfs: replace aio_poll with bdrv_drain, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 13/16] block: only call aio_poll from iothread, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 16/16] aio: convert from RFifoLock to QemuRecMutex, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 14/16] iothread: release AioContext around aio_poll, Paolo Bonzini, 2016/03/16
- [Qemu-devel] [PATCH v2 15/16] qemu-thread: introduce QemuRecMutex, Paolo Bonzini, 2016/03/16
- Re: [Qemu-devel] [PATCH v2 00/16] AioContext fine-grained locking, part 1 of 3, including bdrv_drain rewrite, Stefan Hajnoczi, 2016/03/18