#include "trace.h"
#include "block/blockjob.h"
#include "block/block_int.h"
+#include "sysemu/block-backend.h"
#include "qapi/qmp/qerror.h"
#include "qemu/ratelimit.h"
#include "qemu/bitmap.h"
+#include "qemu/error-report.h"
#define SLICE_TIME 100000000ULL /* ns */
#define MAX_IN_FLIGHT 16
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
MirrorExitData *data = opaque;
AioContext *replace_aio_context = NULL;
+ BlockDriverState *src = s->common.bs;
+
+ /* Make sure that the source BDS doesn't go away before we called
+ * block_job_completed(). */
+ bdrv_ref(src);
if (s->to_replace) {
replace_aio_context = bdrv_get_aio_context(s->to_replace);
if (s->to_replace) {
to_replace = s->to_replace;
}
+
+ /* This was checked in mirror_start_job(), but meanwhile one of the
+ * nodes could have been newly attached to a BlockBackend. */
+ if (to_replace->blk && s->target->blk) {
+ error_report("block job: Can't create node with two BlockBackends");
+ data->ret = -EINVAL;
+ goto out;
+ }
+
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);
- }
+ bdrv_replace_in_backing_chain(to_replace, s->target);
}
+
+out:
if (s->to_replace) {
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
error_free(s->replace_blocker);
aio_context_release(replace_aio_context);
}
g_free(s->replaces);
+ bdrv_op_unblock_all(s->target, s->common.blocker);
bdrv_unref(s->target);
block_job_completed(&s->common, data->ret);
g_free(data);
+ bdrv_drained_end(src);
+ bdrv_unref(src);
}
static void coroutine_fn mirror_run(void *opaque)
*/
bdrv_get_backing_filename(s->target, backing_filename,
sizeof(backing_filename));
- if (backing_filename[0] && !s->target->backing_hd) {
+ if (backing_filename[0] && !s->target->backing) {
ret = bdrv_get_info(s->target, &bdi);
if (ret < 0) {
goto immediate_exit;
g_free(s->cow_bitmap);
g_free(s->in_flight_bitmap);
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
- bdrv_iostatus_disable(s->target);
+ if (s->target->blk) {
+ blk_iostatus_disable(s->target->blk);
+ }
data = g_malloc(sizeof(*data));
data->ret = ret;
+ /* Before we switch to target in mirror_exit, make sure data doesn't
+ * change. */
+ bdrv_drained_begin(s->common.bs);
block_job_defer_to_main_loop(&s->common, mirror_exit, data);
}
{
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
- bdrv_iostatus_reset(s->target);
+ if (s->target->blk) {
+ blk_iostatus_reset(s->target->blk);
+ }
}
static void mirror_complete(BlockJob *job, Error **errp)
Error *local_err = NULL;
int ret;
- ret = bdrv_open_backing_file(s->target, NULL, &local_err);
+ ret = bdrv_open_backing_file(s->target, NULL, "backing", &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
return;
}
if (!s->synced) {
- error_setg(errp, QERR_BLOCK_JOB_NOT_READY,
- bdrv_get_device_name(job->bs));
+ error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
return;
}
bool is_none_mode, BlockDriverState *base)
{
MirrorBlockJob *s;
+ BlockDriverState *replaced_bs;
if (granularity == 0) {
granularity = bdrv_get_default_bitmap_granularity(target);
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
- !bdrv_iostatus_is_enabled(bs)) {
+ (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error");
return;
}
buf_size = DEFAULT_MIRROR_BUF_SIZE;
}
+ /* We can't support this case as long as the block layer can't handle
+ * multiple BlockBackends per BlockDriverState. */
+ if (replaces) {
+ replaced_bs = bdrv_lookup_bs(replaces, replaces, errp);
+ if (replaced_bs == NULL) {
+ return;
+ }
+ } else {
+ replaced_bs = bs;
+ }
+ if (replaced_bs->blk && target->blk) {
+ error_setg(errp, "Can't create node with two BlockBackends");
+ return;
+ }
+
s = block_job_create(driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
if (!s->dirty_bitmap) {
g_free(s->replaces);
- block_job_release(bs);
+ block_job_unref(&s->common);
return;
}
+
+ bdrv_op_block_all(s->target, s->common.blocker);
+
bdrv_set_enable_write_cache(s->target, true);
- bdrv_set_on_error(s->target, on_target_error, on_target_error);
- bdrv_iostatus_enable(s->target);
+ if (s->target->blk) {
+ blk_set_on_error(s->target->blk, on_target_error, on_target_error);
+ blk_iostatus_enable(s->target->blk);
+ }
s->common.co = qemu_coroutine_create(mirror_run);
trace_mirror_start(bs, s, s->common.co, opaque);
qemu_coroutine_enter(s->common.co, s);
return;
}
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
- base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
+ base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
mirror_start_job(bs, target, replaces,
speed, granularity, buf_size,
on_source_error, on_target_error, unmap, cb, opaque, errp,