diff options
Diffstat (limited to 'block/mirror.c')
-rw-r--r-- | block/mirror.c | 16 |
1 files changed, 16 insertions, 0 deletions
diff --git a/block/mirror.c b/block/mirror.c index 010fdafd79..eb9a4cdf56 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -80,6 +80,7 @@ typedef struct MirrorBlockJob { bool initial_zeroing_ongoing; int in_active_write_counter; bool prepared; + bool in_drain; } MirrorBlockJob; typedef struct MirrorBDSOpaque { @@ -683,6 +684,7 @@ static int mirror_exit_common(Job *job) /* The mirror job has no requests in flight any more, but we need to * drain potential other users of the BDS before changing the graph. */ + assert(s->in_drain); bdrv_drained_begin(target_bs); bdrv_replace_node(to_replace, target_bs, &local_err); bdrv_drained_end(target_bs); @@ -721,6 +723,7 @@ static int mirror_exit_common(Job *job) bs_opaque->job = NULL; bdrv_drained_end(src); + s->in_drain = false; bdrv_unref(mirror_top_bs); bdrv_unref(src); @@ -1004,10 +1007,12 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) */ trace_mirror_before_drain(s, cnt); + s->in_drain = true; bdrv_drained_begin(bs); cnt = bdrv_get_dirty_count(s->dirty_bitmap); if (cnt > 0 || mirror_flush(s) < 0) { bdrv_drained_end(bs); + s->in_drain = false; continue; } @@ -1055,6 +1060,7 @@ immediate_exit: bdrv_dirty_iter_free(s->dbi); if (need_drain) { + s->in_drain = true; bdrv_drained_begin(bs); } @@ -1123,6 +1129,16 @@ static void coroutine_fn mirror_pause(Job *job) static bool mirror_drained_poll(BlockJob *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + /* If the job isn't paused nor cancelled, we can't be sure that it won't + * issue more requests. We make an exception if we've reached this point + * from one of our own drain sections, to avoid a deadlock waiting for + * ourselves. + */ + if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) { + return true; + } + return !!s->in_flight; } |