#include "monitor/monitor.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
+#include "qemu/qemu-print.h"
#include "qemu/config-file.h"
#include "qapi/qapi-commands-block.h"
#include "qapi/qapi-commands-transaction.h"
#include "block/trace.h"
#include "sysemu/arch_init.h"
#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
#include "qemu/cutils.h"
#include "qemu/help_option.h"
+#include "qemu/main-loop.h"
#include "qemu/throttle-options.h"
static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
void blockdev_mark_auto_del(BlockBackend *blk)
{
DriveInfo *dinfo = blk_legacy_dinfo(blk);
- BlockDriverState *bs = blk_bs(blk);
- AioContext *aio_context;
+ BlockJob *job;
if (!dinfo) {
return;
}
- if (bs) {
- aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(aio_context);
+ for (job = block_job_next(NULL); job; job = block_job_next(job)) {
+ if (block_job_has_bdrv(job, blk_bs(blk))) {
+ AioContext *aio_context = job->job.aio_context;
+ aio_context_acquire(aio_context);
- if (bs->job) {
- job_cancel(&bs->job->job, false);
- }
+ job_cancel(&job->job, false);
- aio_context_release(aio_context);
+ aio_context_release(aio_context);
+ }
}
dinfo->auto_del = 1;
static void bdrv_format_print(void *opaque, const char *name)
{
- error_printf(" %s", name);
+ qemu_printf(" %s", name);
}
typedef struct {
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
if (is_help_option(buf)) {
- error_printf("Supported formats:");
+ qemu_printf("Supported formats:");
bdrv_iterate_format(bdrv_format_print, NULL, false);
- error_printf("\nSupported formats (read-only):");
+ qemu_printf("\nSupported formats (read-only):");
bdrv_iterate_format(bdrv_format_print, NULL, true);
- error_printf("\n");
+ qemu_printf("\n");
goto early_err;
}
if ((!file || !*file) && !qdict_size(bs_opts)) {
BlockBackendRootState *blk_rs;
- blk = blk_new(0, BLK_PERM_ALL);
+ blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
blk_rs = blk_get_root_state(blk);
blk_rs->open_flags = bdrv_flags;
blk_rs->read_only = read_only;
DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action;
AioContext *aio_context;
+ int ret;
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
* purpose but a different set of parameters */
s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
if (node_name && !snapshot_node_name) {
- error_setg(errp, "New snapshot node name missing");
+ error_setg(errp, "New overlay node name missing");
goto out;
}
if (snapshot_node_name &&
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
- error_setg(errp, "New snapshot node name already in use");
+ error_setg(errp, "New overlay node name already in use");
goto out;
}
}
if (bdrv_has_blk(state->new_bs)) {
- error_setg(errp, "The snapshot is already in use");
+ error_setg(errp, "The overlay is already in use");
goto out;
}
}
if (state->new_bs->backing != NULL) {
- error_setg(errp, "The snapshot already has a backing image");
+ error_setg(errp, "The overlay already has a backing image");
goto out;
}
if (!state->new_bs->drv->supports_backing) {
- error_setg(errp, "The snapshot does not support backing images");
+ error_setg(errp, "The overlay does not support backing images");
goto out;
}
- bdrv_set_aio_context(state->new_bs, aio_context);
+ ret = bdrv_try_set_aio_context(state->new_bs, aio_context, errp);
+ if (ret < 0) {
+ goto out;
+ }
/* This removes our old bs and adds the new bs. This is an operation that
* can fail, so we need to do it in .prepare; undoing it for abort is
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
backup = common->action->u.drive_backup.data;
- bs = qmp_get_root_bs(backup->device, errp);
+ bs = bdrv_lookup_bs(backup->device, backup->device, errp);
if (!bs) {
return;
}
}
aio_context = bdrv_get_aio_context(bs);
- if (aio_context != bdrv_get_aio_context(target)) {
- error_setg(errp, "Backup between two IO threads is not implemented");
- return;
- }
aio_context_acquire(aio_context);
state->bs = bs;
}
}
-static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node,
- const char *target,
- strList *bitmaps,
- HBitmap **backup,
- Error **errp);
+static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(
+ const char *node, const char *target,
+ BlockDirtyBitmapMergeSourceList *bitmaps,
+ HBitmap **backup, Error **errp);
static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
Error **errp)
errp);
}
+static BdrvDirtyBitmap *do_block_dirty_bitmap_remove(
+ const char *node, const char *name, bool release,
+ BlockDriverState **bitmap_bs, Error **errp);
+
+static void block_dirty_bitmap_remove_prepare(BlkActionState *common,
+ Error **errp)
+{
+ BlockDirtyBitmap *action;
+ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
+ common, common);
+
+ if (action_check_completion_mode(common, errp) < 0) {
+ return;
+ }
+
+ action = common->action->u.block_dirty_bitmap_remove.data;
+
+ state->bitmap = do_block_dirty_bitmap_remove(action->node, action->name,
+ false, &state->bs, errp);
+ if (state->bitmap) {
+ bdrv_dirty_bitmap_skip_store(state->bitmap, true);
+ bdrv_dirty_bitmap_set_busy(state->bitmap, true);
+ }
+}
+
+static void block_dirty_bitmap_remove_abort(BlkActionState *common)
+{
+ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
+ common, common);
+
+ if (state->bitmap) {
+ bdrv_dirty_bitmap_skip_store(state->bitmap, false);
+ bdrv_dirty_bitmap_set_busy(state->bitmap, false);
+ }
+}
+
+static void block_dirty_bitmap_remove_commit(BlkActionState *common)
+{
+ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
+ common, common);
+
+ bdrv_dirty_bitmap_set_busy(state->bitmap, false);
+ bdrv_release_dirty_bitmap(state->bs, state->bitmap);
+}
+
static void abort_prepare(BlkActionState *common, Error **errp)
{
error_setg(errp, "Transaction aborted using Abort action");
.commit = block_dirty_bitmap_free_backup,
.abort = block_dirty_bitmap_restore,
},
+ [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE] = {
+ .instance_size = sizeof(BlockDirtyBitmapState),
+ .prepare = block_dirty_bitmap_remove_prepare,
+ .commit = block_dirty_bitmap_remove_commit,
+ .abort = block_dirty_bitmap_remove_abort,
+ },
/* Where are transactions for MIRROR, COMMIT and STREAM?
* Although these blockjobs use transaction callbacks like the backup job,
* these jobs do not necessarily adhere to transaction semantics.
{
BlockDriverState *bs;
BdrvDirtyBitmap *bitmap;
- AioContext *aio_context = NULL;
if (!name || name[0] == '\0') {
error_setg(errp, "Bitmap name cannot be empty");
}
if (persistent) {
- aio_context = bdrv_get_aio_context(bs);
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+ bool ok;
+
aio_context_acquire(aio_context);
- if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) {
- goto out;
+ ok = bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp);
+ aio_context_release(aio_context);
+ if (!ok) {
+ return;
}
}
bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
if (bitmap == NULL) {
- goto out;
+ return;
}
if (disabled) {
}
bdrv_dirty_bitmap_set_persistence(bitmap, persistent);
- out:
- if (aio_context) {
- aio_context_release(aio_context);
- }
}
-void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
- Error **errp)
+static BdrvDirtyBitmap *do_block_dirty_bitmap_remove(
+ const char *node, const char *name, bool release,
+ BlockDriverState **bitmap_bs, Error **errp)
{
BlockDriverState *bs;
BdrvDirtyBitmap *bitmap;
- Error *local_err = NULL;
- AioContext *aio_context = NULL;
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
if (!bitmap || !bs) {
- return;
+ return NULL;
}
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO,
errp)) {
- return;
+ return NULL;
}
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
- aio_context = bdrv_get_aio_context(bs);
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+ Error *local_err = NULL;
+
aio_context_acquire(aio_context);
bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err);
+ aio_context_release(aio_context);
+
if (local_err != NULL) {
error_propagate(errp, local_err);
- goto out;
+ return NULL;
}
}
- bdrv_release_dirty_bitmap(bs, bitmap);
- out:
- if (aio_context) {
- aio_context_release(aio_context);
+ if (release) {
+ bdrv_release_dirty_bitmap(bs, bitmap);
+ }
+
+ if (bitmap_bs) {
+ *bitmap_bs = bs;
}
+
+ return release ? NULL : bitmap;
+}
+
+void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
+ Error **errp)
+{
+ do_block_dirty_bitmap_remove(node, name, true, NULL, errp);
}
/**
bdrv_disable_dirty_bitmap(bitmap);
}
-static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node,
- const char *target,
- strList *bitmaps,
- HBitmap **backup,
- Error **errp)
+static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(
+ const char *node, const char *target,
+ BlockDirtyBitmapMergeSourceList *bitmaps,
+ HBitmap **backup, Error **errp)
{
BlockDriverState *bs;
BdrvDirtyBitmap *dst, *src, *anon;
- strList *lst;
+ BlockDirtyBitmapMergeSourceList *lst;
Error *local_err = NULL;
dst = block_dirty_bitmap_lookup(node, target, &bs, errp);
}
for (lst = bitmaps; lst; lst = lst->next) {
- src = bdrv_find_dirty_bitmap(bs, lst->value);
- if (!src) {
- error_setg(errp, "Dirty bitmap '%s' not found", lst->value);
- dst = NULL;
- goto out;
+ switch (lst->value->type) {
+ const char *name, *node;
+ case QTYPE_QSTRING:
+ name = lst->value->u.local;
+ src = bdrv_find_dirty_bitmap(bs, name);
+ if (!src) {
+ error_setg(errp, "Dirty bitmap '%s' not found", name);
+ dst = NULL;
+ goto out;
+ }
+ break;
+ case QTYPE_QDICT:
+ node = lst->value->u.external.node;
+ name = lst->value->u.external.name;
+ src = block_dirty_bitmap_lookup(node, name, NULL, errp);
+ if (!src) {
+ dst = NULL;
+ goto out;
+ }
+ break;
+ default:
+ abort();
}
bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err);
}
void qmp_block_dirty_bitmap_merge(const char *node, const char *target,
- strList *bitmaps, Error **errp)
+ BlockDirtyBitmapMergeSourceList *bitmaps,
+ Error **errp)
{
do_block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp);
}
goto out;
}
- blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
+ blk = blk_new(bdrv_get_aio_context(bs), BLK_PERM_RESIZE, BLK_PERM_ALL);
ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) {
goto out;
goto out;
}
- trace_qmp_block_stream(bs, bs->job);
+ trace_qmp_block_stream(bs);
out:
aio_context_release(aio_context);
aio_context_release(aio_context);
}
-static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
- Error **errp)
+/* Common QMP interface for drive-backup and blockdev-backup */
+static BlockJob *do_backup_common(BackupCommon *backup,
+ BlockDriverState *bs,
+ BlockDriverState *target_bs,
+ AioContext *aio_context,
+ JobTxn *txn, Error **errp)
{
- BlockDriverState *bs;
- BlockDriverState *target_bs;
- BlockDriverState *source = NULL;
BlockJob *job = NULL;
BdrvDirtyBitmap *bmap = NULL;
- AioContext *aio_context;
- QDict *options = NULL;
- Error *local_err = NULL;
- int flags, job_flags = JOB_DEFAULT;
- int64_t size;
- bool set_backing_hd = false;
+ int job_flags = JOB_DEFAULT;
+ int ret;
if (!backup->has_speed) {
backup->speed = 0;
if (!backup->has_on_target_error) {
backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
}
- if (!backup->has_mode) {
- backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
- }
if (!backup->has_job_id) {
backup->job_id = NULL;
}
backup->compress = false;
}
- bs = qmp_get_root_bs(backup->device, errp);
+ ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
+ (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) {
+ /* done before desugaring 'incremental' to print the right message */
+ if (!backup->has_bitmap) {
+ error_setg(errp, "must provide a valid bitmap name for "
+ "'%s' sync mode", MirrorSyncMode_str(backup->sync));
+ return NULL;
+ }
+ }
+
+ if (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL) {
+ if (backup->has_bitmap_mode &&
+ backup->bitmap_mode != BITMAP_SYNC_MODE_ON_SUCCESS) {
+ error_setg(errp, "Bitmap sync mode must be '%s' "
+ "when using sync mode '%s'",
+ BitmapSyncMode_str(BITMAP_SYNC_MODE_ON_SUCCESS),
+ MirrorSyncMode_str(backup->sync));
+ return NULL;
+ }
+ backup->has_bitmap_mode = true;
+ backup->sync = MIRROR_SYNC_MODE_BITMAP;
+ backup->bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS;
+ }
+
+ if (backup->has_bitmap) {
+ bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
+ if (!bmap) {
+ error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
+ return NULL;
+ }
+ if (!backup->has_bitmap_mode) {
+ error_setg(errp, "Bitmap sync mode must be given "
+ "when providing a bitmap");
+ return NULL;
+ }
+ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) {
+ return NULL;
+ }
+
+ /* This does not produce a useful bitmap artifact: */
+ if (backup->sync == MIRROR_SYNC_MODE_NONE) {
+ error_setg(errp, "sync mode '%s' does not produce meaningful bitmap"
+ " outputs", MirrorSyncMode_str(backup->sync));
+ return NULL;
+ }
+
+ /* If the bitmap isn't used for input or output, this is useless: */
+ if (backup->bitmap_mode == BITMAP_SYNC_MODE_NEVER &&
+ backup->sync != MIRROR_SYNC_MODE_BITMAP) {
+ error_setg(errp, "Bitmap sync mode '%s' has no meaningful effect"
+ " when combined with sync mode '%s'",
+ BitmapSyncMode_str(backup->bitmap_mode),
+ MirrorSyncMode_str(backup->sync));
+ return NULL;
+ }
+ }
+
+ if (!backup->has_bitmap && backup->has_bitmap_mode) {
+ error_setg(errp, "Cannot specify bitmap sync mode without a bitmap");
+ return NULL;
+ }
+
+ if (!backup->auto_finalize) {
+ job_flags |= JOB_MANUAL_FINALIZE;
+ }
+ if (!backup->auto_dismiss) {
+ job_flags |= JOB_MANUAL_DISMISS;
+ }
+
+ job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
+ backup->sync, bmap, backup->bitmap_mode,
+ backup->compress,
+ backup->on_source_error,
+ backup->on_target_error,
+ job_flags, NULL, NULL, txn, errp);
+ return job;
+}
+
+static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ BlockDriverState *target_bs;
+ BlockDriverState *source = NULL;
+ BlockJob *job = NULL;
+ AioContext *aio_context;
+ QDict *options;
+ Error *local_err = NULL;
+ int flags;
+ int64_t size;
+ bool set_backing_hd = false;
+
+ if (!backup->has_mode) {
+ backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ }
+
+ bs = bdrv_lookup_bs(backup->device, backup->device, errp);
if (!bs) {
return NULL;
}
+ if (!bs->drv) {
+ error_setg(errp, "Device has no medium");
+ return NULL;
+ }
+
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
goto out;
}
+ options = qdict_new();
+ qdict_put_str(options, "discard", "unmap");
+ qdict_put_str(options, "detect-zeroes", "unmap");
if (backup->format) {
- if (!options) {
- options = qdict_new();
- }
qdict_put_str(options, "driver", backup->format);
}
goto out;
}
- bdrv_set_aio_context(target_bs, aio_context);
-
if (set_backing_hd) {
bdrv_set_backing_hd(target_bs, source, &local_err);
if (local_err) {
- bdrv_unref(target_bs);
- goto out;
+ goto unref;
}
}
- if (backup->has_bitmap) {
- bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
- if (!bmap) {
- error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
- bdrv_unref(target_bs);
- goto out;
- }
- if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) {
- goto out;
- }
- }
- if (!backup->auto_finalize) {
- job_flags |= JOB_MANUAL_FINALIZE;
- }
- if (!backup->auto_dismiss) {
- job_flags |= JOB_MANUAL_DISMISS;
- }
+ job = do_backup_common(qapi_DriveBackup_base(backup),
+ bs, target_bs, aio_context, txn, errp);
- job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
- backup->sync, bmap, backup->compress,
- backup->on_source_error, backup->on_target_error,
- job_flags, NULL, NULL, txn, &local_err);
+unref:
bdrv_unref(target_bs);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- goto out;
- }
-
out:
aio_context_release(aio_context);
return job;
{
BlockDriverState *bs;
BlockDriverState *target_bs;
- Error *local_err = NULL;
- BdrvDirtyBitmap *bmap = NULL;
AioContext *aio_context;
- BlockJob *job = NULL;
- int job_flags = JOB_DEFAULT;
-
- if (!backup->has_speed) {
- backup->speed = 0;
- }
- if (!backup->has_on_source_error) {
- backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT;
- }
- if (!backup->has_on_target_error) {
- backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
- }
- if (!backup->has_job_id) {
- backup->job_id = NULL;
- }
- if (!backup->has_auto_finalize) {
- backup->auto_finalize = true;
- }
- if (!backup->has_auto_dismiss) {
- backup->auto_dismiss = true;
- }
- if (!backup->has_compress) {
- backup->compress = false;
- }
+ BlockJob *job;
bs = bdrv_lookup_bs(backup->device, backup->device, errp);
if (!bs) {
return NULL;
}
- aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(aio_context);
-
target_bs = bdrv_lookup_bs(backup->target, backup->target, errp);
if (!target_bs) {
- goto out;
+ return NULL;
}
- if (bdrv_get_aio_context(target_bs) != aio_context) {
- if (!bdrv_has_blk(target_bs)) {
- /* The target BDS is not attached, we can safely move it to another
- * AioContext. */
- bdrv_set_aio_context(target_bs, aio_context);
- } else {
- error_setg(errp, "Target is attached to a different thread from "
- "source.");
- goto out;
- }
- }
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
- if (backup->has_bitmap) {
- bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
- if (!bmap) {
- error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
- goto out;
- }
- if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) {
- goto out;
- }
- }
+ job = do_backup_common(qapi_BlockdevBackup_base(backup),
+ bs, target_bs, aio_context, txn, errp);
- if (!backup->auto_finalize) {
- job_flags |= JOB_MANUAL_FINALIZE;
- }
- if (!backup->auto_dismiss) {
- job_flags |= JOB_MANUAL_DISMISS;
- }
- job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
- backup->sync, bmap, backup->compress,
- backup->on_source_error, backup->on_target_error,
- job_flags, NULL, NULL, txn, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- }
-out:
aio_context_release(aio_context);
return job;
}
bool has_replaces, const char *replaces,
enum MirrorSyncMode sync,
BlockMirrorBackingMode backing_mode,
+ bool zero_target,
bool has_speed, int64_t speed,
bool has_granularity, uint32_t granularity,
bool has_buf_size, int64_t buf_size,
*/
mirror_start(job_id, bs, target,
has_replaces ? replaces : NULL, job_flags,
- speed, granularity, buf_size, sync, backing_mode,
+ speed, granularity, buf_size, sync, backing_mode, zero_target,
on_source_error, on_target_error, unmap, filter_node_name,
copy_mode, errp);
}
int flags;
int64_t size;
const char *format = arg->format;
+ bool zero_target;
+ int ret;
bs = qmp_get_root_bs(arg->device, errp);
if (!bs) {
goto out;
}
- bdrv_set_aio_context(target_bs, aio_context);
+ zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL &&
+ (arg->mode == NEW_IMAGE_MODE_EXISTING ||
+ !bdrv_has_zero_init(target_bs)));
+
+ ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
+ if (ret < 0) {
+ bdrv_unref(target_bs);
+ goto out;
+ }
blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs,
arg->has_replaces, arg->replaces, arg->sync,
- backing_mode, arg->has_speed, arg->speed,
+ backing_mode, zero_target,
+ arg->has_speed, arg->speed,
arg->has_granularity, arg->granularity,
arg->has_buf_size, arg->buf_size,
arg->has_on_source_error, arg->on_source_error,
AioContext *aio_context;
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
Error *local_err = NULL;
+ bool zero_target;
+ int ret;
bs = qmp_get_root_bs(device, errp);
if (!bs) {
return;
}
+ zero_target = (sync == MIRROR_SYNC_MODE_FULL);
+
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- bdrv_set_aio_context(target_bs, aio_context);
+ ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
+ if (ret < 0) {
+ goto out;
+ }
blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
has_replaces, replaces, sync, backing_mode,
- has_speed, speed,
+ zero_target, has_speed, speed,
has_granularity, granularity,
has_buf_size, buf_size,
has_on_source_error, on_source_error,
has_auto_dismiss, auto_dismiss,
&local_err);
error_propagate(errp, local_err);
-
+out:
aio_context_release(aio_context);
}
old_context = bdrv_get_aio_context(bs);
aio_context_acquire(old_context);
- bdrv_set_aio_context(bs, new_context);
+ bdrv_try_set_aio_context(bs, new_context, errp);
aio_context_release(old_context);
}