Patchwork [3/8] block: allow doing I/O in a job after cancellation

login
register
mail settings
Submitter Paolo Bonzini
Date April 13, 2012, 4:23 p.m.
Message ID <1334334198-30899-4-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/152347/
State New
Headers show

Comments

Paolo Bonzini - April 13, 2012, 4:23 p.m.
Track the coroutine that executes the job, so that the wait can be
cancelled before block_job_cancel restarts.  This also gives to the
coroutine an opportunity to flip job->busy to true, and submit new
I/O before exiting.  block_job_cancel_sync will wait for job->busy
to become false again.

Also drain the I/O *before* canceling the job, so that all I/O from
the guest is visible to the job.

This is needed for mirroring.  Once mirroring reaches "steady state"
(i.e. all data from the source is also in the target, except for new
writes coming in from the guest), cancellation of the job will keep
the disks synchronized.  The job thus requires to handle cancellation
with care, and this patch provides the infrastructure.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block.c        |    8 ++++++++
 block/stream.c |    7 +++----
 block_int.h    |   12 ++++++++----
 3 files changed, 19 insertions(+), 8 deletions(-)

Patch

diff --git a/block.c b/block.c
index 1eb82f2..c272421 100644
--- a/block.c
+++ b/block.c
@@ -4255,7 +4255,15 @@  int block_job_set_speed(BlockJob *job, int64_t value)
 
 void block_job_cancel(BlockJob *job)
 {
+    /* Complete all guest I/O before cancelling the job, so that if the
+     * job chooses to complete itself it will do so with a consistent
+     * view of the disk.
+     */
+    bdrv_drain_all();
     job->cancelled = true;
+    if (job->co && !job->busy) {
+        qemu_coroutine_enter(job->co, NULL);
+    }
 }
 
 bool block_job_is_cancelled(BlockJob *job)
diff --git a/block/stream.c b/block/stream.c
index 0116450..d38f30a 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -191,7 +191,6 @@  int stream_start(BlockDriverState *bs, BlockDriverState *base,
                  void *opaque)
 {
     StreamBlockJob *s;
-    Coroutine *co;
 
     s = block_job_create(&stream_job_type, bs, cb, opaque);
     if (!s) {
@@ -203,8 +202,8 @@  int stream_start(BlockDriverState *bs, BlockDriverState *base,
         pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
     }
 
-    co = qemu_coroutine_create(stream_run);
-    trace_stream_start(bs, base, s, co, opaque);
-    qemu_coroutine_enter(co, s);
+    s->common.co = qemu_coroutine_create(stream_run);
+    trace_stream_start(bs, base, s, s->common.co, opaque);
+    qemu_coroutine_enter(s->common.co, s);
     return 0;
 }
diff --git a/block_int.h b/block_int.h
index 58e3eea..eae24d2 100644
--- a/block_int.h
+++ b/block_int.h
@@ -94,6 +94,12 @@  struct BlockJob {
     BlockDriverState *bs;
 
     /**
+     * The coroutine that executes the job.  If not NULL, it is
+     * reentered when busy is false and the job is cancelled.
+     */
+    Coroutine *co;
+
+    /**
      * Set to true if the job should cancel itself.  The flag must
      * always be tested just before toggling the busy flag from false
      * to true.  After a job has detected that the cancelled flag is
@@ -104,10 +110,8 @@  struct BlockJob {
 
     /**
      * Set to false by the job while it is in a quiescent state, where
-     * no I/O is pending and cancellation can be processed without
-     * issuing new I/O.  The busy flag must be set to false when the
-     * job goes to sleep on any condition that is not detected by
-     * #qemu_aio_wait, such as a timer.
+     * no I/O is pending and the job goes to sleep on any condition
+     * that is not detected by #qemu_aio_wait, such as a timer.
      */
     bool busy;