diff options
-rw-r--r-- | block/mirror.c | 45 |
1 files changed, 40 insertions, 5 deletions
diff --git a/block/mirror.c b/block/mirror.c index b353798e8f..6320f6a2e5 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -32,6 +32,8 @@ typedef struct MirrorBlockJob { RateLimit limit; BlockDriverState *target; MirrorSyncMode mode; + bool synced; + bool should_complete; int64_t sector_num; uint8_t *buf; } MirrorBlockJob; @@ -70,7 +72,6 @@ static void coroutine_fn mirror_run(void *opaque) int64_t sector_num, end; int ret = 0; int n; - bool synced = false; if (block_job_is_cancelled(&s->common)) { goto immediate_exit; @@ -136,9 +137,14 @@ static void coroutine_fn mirror_run(void *opaque) * report completion. This way, block-job-cancel will leave * the target in a consistent state. */ - synced = true; s->common.offset = end * BDRV_SECTOR_SIZE; - should_complete = block_job_is_cancelled(&s->common); + if (!s->synced) { + block_job_ready(&s->common); + s->synced = true; + } + + should_complete = s->should_complete || + block_job_is_cancelled(&s->common); cnt = bdrv_get_dirty_count(bs); } @@ -157,8 +163,8 @@ static void coroutine_fn mirror_run(void *opaque) } ret = 0; - trace_mirror_before_sleep(s, cnt, synced); - if (!synced) { + trace_mirror_before_sleep(s, cnt, s->synced); + if (!s->synced) { /* Publish progress */ s->common.offset = end * BDRV_SECTOR_SIZE - cnt * BLOCK_SIZE; @@ -191,6 +197,12 @@ static void coroutine_fn mirror_run(void *opaque) immediate_exit: g_free(s->buf); bdrv_set_dirty_tracking(bs, false); + if (s->should_complete && ret == 0) { + if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) { + bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL); + } + bdrv_swap(s->target, s->common.bs); + } bdrv_close(s->target); bdrv_delete(s->target); block_job_completed(&s->common, ret); @@ -207,10 +219,33 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); } +static void mirror_complete(BlockJob *job, Error **errp) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + int ret; + + ret = bdrv_open_backing_file(s->target); + if (ret < 0) { + char backing_filename[PATH_MAX]; + bdrv_get_full_backing_filename(s->target, backing_filename, + sizeof(backing_filename)); + error_set(errp, QERR_OPEN_FILE_FAILED, backing_filename); + return; + } + if (!s->synced) { + error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name); + return; + } + + s->should_complete = true; + block_job_resume(job); +} + static BlockJobType mirror_job_type = { .instance_size = sizeof(MirrorBlockJob), .job_type = "mirror", .set_speed = mirror_set_speed, + .complete = mirror_complete, }; void mirror_start(BlockDriverState *bs, BlockDriverState *target, |