aboutsummaryrefslogtreecommitdiff
path: root/block/mirror.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/mirror.c')
-rw-r--r--block/mirror.c16
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;
}