diff --git a/block.c b/block.c index a909b9d749..dacd8815d3 100644 --- a/block.c +++ b/block.c @@ -5850,13 +5850,19 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate) BlockDriverState *check_to_replace_node(const char *node_name, Error **errp) { BlockDriverState *to_replace_bs = bdrv_find_node(node_name); + AioContext *aio_context; + if (!to_replace_bs) { error_setg(errp, "Node name '%s' not found", node_name); return NULL; } + aio_context = bdrv_get_aio_context(to_replace_bs); + aio_context_acquire(aio_context); + if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) { - return NULL; + to_replace_bs = NULL; + goto out; } /* We don't want arbitrary node of the BDS chain to be replaced only the top @@ -5866,9 +5872,12 @@ BlockDriverState *check_to_replace_node(const char *node_name, Error **errp) */ if (!bdrv_is_first_non_filter(to_replace_bs)) { error_setg(errp, "Only top most non filter can be replaced"); - return NULL; + to_replace_bs = NULL; + goto out; } +out: + aio_context_release(aio_context); return to_replace_bs; } diff --git a/block/mirror.c b/block/mirror.c index 2a1acfe26f..2c6dd2a4c1 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -321,9 +321,56 @@ static void mirror_drain(MirrorBlockJob *s) } } +typedef struct { + int ret; +} MirrorExitData; + +static void mirror_exit(BlockJob *job, void *opaque) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorExitData *data = opaque; + AioContext *replace_aio_context = NULL; + + if (s->to_replace) { + replace_aio_context = bdrv_get_aio_context(s->to_replace); + aio_context_acquire(replace_aio_context); + } + + if (s->should_complete && data->ret == 0) { + BlockDriverState *to_replace = s->common.bs; + if (s->to_replace) { + to_replace = s->to_replace; + } + if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { + bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); + } + bdrv_swap(s->target, to_replace); + if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { + /* drop the bs loop chain formed by the swap: break the loop then + * trigger the unref from the top one */ + BlockDriverState *p = s->base->backing_hd; + bdrv_set_backing_hd(s->base, NULL); + bdrv_unref(p); + } + } + if (s->to_replace) { + bdrv_op_unblock_all(s->to_replace, s->replace_blocker); + error_free(s->replace_blocker); + bdrv_unref(s->to_replace); + } + if (replace_aio_context) { + aio_context_release(replace_aio_context); + } + g_free(s->replaces); + bdrv_unref(s->target); + block_job_completed(&s->common, data->ret); + g_free(data); +} + static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; + MirrorExitData *data; BlockDriverState *bs = s->common.bs; int64_t sector_num, end, sectors_per_chunk, length; uint64_t last_pause_ns; @@ -479,7 +526,7 @@ static void coroutine_fn mirror_run(void *opaque) * mirror_populate runs. */ trace_mirror_before_drain(s, cnt); - bdrv_drain_all(); + bdrv_drain(bs); cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap); } @@ -520,31 +567,10 @@ immediate_exit: g_free(s->in_flight_bitmap); bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); bdrv_iostatus_disable(s->target); - if (s->should_complete && ret == 0) { - BlockDriverState *to_replace = s->common.bs; - if (s->to_replace) { - to_replace = s->to_replace; - } - if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { - bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); - } - bdrv_swap(s->target, to_replace); - if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { - /* drop the bs loop chain formed by the swap: break the loop then - * trigger the unref from the top one */ - BlockDriverState *p = s->base->backing_hd; - bdrv_set_backing_hd(s->base, NULL); - bdrv_unref(p); - } - } - if (s->to_replace) { - bdrv_op_unblock_all(s->to_replace, s->replace_blocker); - error_free(s->replace_blocker); - bdrv_unref(s->to_replace); - } - g_free(s->replaces); - bdrv_unref(s->target); - block_job_completed(&s->common, ret); + + data = g_malloc(sizeof(*data)); + data->ret = ret; + block_job_defer_to_main_loop(&s->common, mirror_exit, data); } static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) @@ -584,16 +610,23 @@ static void mirror_complete(BlockJob *job, Error **errp) /* check the target bs is not blocked and block all operations on it */ if (s->replaces) { + AioContext *replace_aio_context; + s->to_replace = check_to_replace_node(s->replaces, &local_err); if (!s->to_replace) { error_propagate(errp, local_err); return; } + replace_aio_context = bdrv_get_aio_context(s->to_replace); + aio_context_acquire(replace_aio_context); + error_setg(&s->replace_blocker, "block device is in use by block-job-complete"); bdrv_op_block_all(s->to_replace, s->replace_blocker); bdrv_ref(s->to_replace); + + aio_context_release(replace_aio_context); } s->should_complete = true; diff --git a/blockdev.c b/blockdev.c index 6e43d2e3a8..13763225ee 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2245,6 +2245,7 @@ void qmp_drive_mirror(const char *device, const char *target, { BlockDriverState *bs; BlockDriverState *source, *target_bs; + AioContext *aio_context; BlockDriver *drv = NULL; Error *local_err = NULL; QDict *options = NULL; @@ -2287,9 +2288,12 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); - return; + goto out; } if (!has_format) { @@ -2299,12 +2303,12 @@ void qmp_drive_mirror(const char *device, const char *target, drv = bdrv_find_format(format); if (!drv) { error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; + goto out; } } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) { - return; + goto out; } flags = bs->open_flags | BDRV_O_RDWR; @@ -2319,29 +2323,36 @@ void qmp_drive_mirror(const char *device, const char *target, size = bdrv_getlength(bs); if (size < 0) { error_setg_errno(errp, -size, "bdrv_getlength failed"); - return; + goto out; } if (has_replaces) { BlockDriverState *to_replace_bs; + AioContext *replace_aio_context; + int64_t replace_size; if (!has_node_name) { error_setg(errp, "a node-name must be provided when replacing a" " named node of the graph"); - return; + goto out; } to_replace_bs = check_to_replace_node(replaces, &local_err); if (!to_replace_bs) { error_propagate(errp, local_err); - return; + goto out; } - if (size != bdrv_getlength(to_replace_bs)) { + replace_aio_context = bdrv_get_aio_context(to_replace_bs); + aio_context_acquire(replace_aio_context); + replace_size = bdrv_getlength(to_replace_bs); + aio_context_release(replace_aio_context); + + if (size != replace_size) { error_setg(errp, "cannot replace image with a mirror image of " "different size"); - return; + goto out; } } @@ -2370,7 +2381,7 @@ void qmp_drive_mirror(const char *device, const char *target, if (local_err) { error_propagate(errp, local_err); - return; + goto out; } if (has_node_name) { @@ -2386,9 +2397,11 @@ void qmp_drive_mirror(const char *device, const char *target, flags | BDRV_O_NO_BACKING, drv, &local_err); if (ret < 0) { error_propagate(errp, local_err); - return; + goto out; } + bdrv_set_aio_context(target_bs, aio_context); + /* pass the node name to replace to mirror start since it's loose coupling * and will allow to check whether the node still exist at mirror completion */ @@ -2400,8 +2413,11 @@ void qmp_drive_mirror(const char *device, const char *target, if (local_err != NULL) { bdrv_unref(target_bs); error_propagate(errp, local_err); - return; + goto out; } + +out: + aio_context_release(aio_context); } /* Get the block job for a given device name and acquire its AioContext */