#include "qemu/error-report.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
-#include "qapi/qmp/types.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qnum.h"
+#include "qapi/qmp/qstring.h"
#include "qapi-visit.h"
+#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
+#include "qapi/qmp/qlist.h"
#include "qapi/qobject-output-visitor.h"
-#include "qapi/util.h"
#include "sysemu/sysemu.h"
+#include "sysemu/iothread.h"
#include "block/block_int.h"
#include "qmp-commands.h"
#include "block/trace.h"
static int do_open_tray(const char *blk_name, const char *qdev_id,
bool force, Error **errp);
+static void blockdev_remove_medium(bool has_device, const char *device,
+ bool has_id, const char *id, Error **errp);
+static void blockdev_insert_medium(bool has_device, const char *device,
+ bool has_id, const char *id,
+ const char *node_name, Error **errp);
static const char *const if_name[IF_COUNT] = {
[IF_NONE] = "none",
if (detect_zeroes) {
*detect_zeroes =
- qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
+ qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup,
qemu_opt_get(opts, "detect-zeroes"),
- BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX,
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
&local_error);
if (local_error) {
.name = "trans",
.type = QEMU_OPT_STRING,
.help = "chs translation (auto, lba, none)",
- },{
- .name = "boot",
- .type = QEMU_OPT_BOOL,
- .help = "(deprecated, ignored)",
},{
.name = "addr",
.type = QEMU_OPT_STRING,
goto fail;
}
- /* Deprecated option boot=[on|off] */
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
- "ignored. Future versions will reject this parameter. Please "
- "update your scripts.\n");
- }
-
/* Other deprecated options */
if (!qtest_enabled()) {
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false);
if (read_only && copy_on_read) {
- error_report("warning: disabling copy-on-read on read-only drive");
+ warn_report("disabling copy-on-read on read-only drive");
copy_on_read = false;
}
typedef struct InternalSnapshotState {
BlkActionState common;
BlockDriverState *bs;
- AioContext *aio_context;
QEMUSnapshotInfo sn;
bool created;
} InternalSnapshotState;
error_setg(errp,
"Action '%s' does not support Transaction property "
"completion-mode = %s",
- TransactionActionKind_lookup[s->action->type],
- ActionCompletionMode_lookup[s->txn_props->completion_mode]);
+ TransactionActionKind_str(s->action->type),
+ ActionCompletionMode_str(s->txn_props->completion_mode));
return -1;
}
return 0;
qemu_timeval tv;
BlockdevSnapshotInternal *internal;
InternalSnapshotState *state;
+ AioContext *aio_context;
int ret1;
g_assert(common->action->type ==
return;
}
- /* AioContext is released in .clean() */
- state->aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(state->aio_context);
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
state->bs = bs;
+
+ /* Paired with .clean() */
bdrv_drained_begin(bs);
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
- return;
+ goto out;
}
if (bdrv_is_read_only(bs)) {
error_setg(errp, "Device '%s' is read only", device);
- return;
+ goto out;
}
if (!bdrv_can_snapshot(bs)) {
error_setg(errp, "Block format '%s' used by device '%s' "
"does not support internal snapshots",
bs->drv->format_name, device);
- return;
+ goto out;
}
if (!strlen(name)) {
error_setg(errp, "Name is empty");
- return;
+ goto out;
}
/* check whether a snapshot with name exist */
&local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
} else if (ret) {
error_setg(errp,
"Snapshot with name '%s' already exists on device '%s'",
name, device);
- return;
+ goto out;
}
/* 3. take the snapshot */
error_setg_errno(errp, -ret1,
"Failed to create snapshot '%s' on device '%s'",
name, device);
- return;
+ goto out;
}
/* 4. succeed, mark a snapshot is created */
state->created = true;
+
+out:
+ aio_context_release(aio_context);
}
static void internal_snapshot_abort(BlkActionState *common)
DO_UPCAST(InternalSnapshotState, common, common);
BlockDriverState *bs = state->bs;
QEMUSnapshotInfo *sn = &state->sn;
+ AioContext *aio_context;
Error *local_error = NULL;
if (!state->created) {
return;
}
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
error_reportf_err(local_error,
"Failed to delete snapshot with id '%s' and "
sn->id_str, sn->name,
bdrv_get_device_name(bs));
}
+
+ aio_context_release(aio_context);
}
static void internal_snapshot_clean(BlkActionState *common)
{
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
common, common);
+ AioContext *aio_context;
- if (state->aio_context) {
- if (state->bs) {
- bdrv_drained_end(state->bs);
- }
- aio_context_release(state->aio_context);
+ if (!state->bs) {
+ return;
}
+
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
+ bdrv_drained_end(state->bs);
+
+ aio_context_release(aio_context);
}
/* external snapshot private data */
BlkActionState common;
BlockDriverState *old_bs;
BlockDriverState *new_bs;
- AioContext *aio_context;
bool overlay_appended;
} ExternalSnapshotState;
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action;
+ AioContext *aio_context;
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
* purpose but a different set of parameters */
return;
}
- /* Acquire AioContext now so any threads operating on old_bs stop */
- state->aio_context = bdrv_get_aio_context(state->old_bs);
- aio_context_acquire(state->aio_context);
+ aio_context = bdrv_get_aio_context(state->old_bs);
+ aio_context_acquire(aio_context);
+
+ /* Paired with .clean() */
bdrv_drained_begin(state->old_bs);
if (!bdrv_is_inserted(state->old_bs)) {
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
- return;
+ goto out;
}
if (bdrv_op_is_blocked(state->old_bs,
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
- return;
+ goto out;
}
if (!bdrv_is_read_only(state->old_bs)) {
if (bdrv_flush(state->old_bs)) {
error_setg(errp, QERR_IO_ERROR);
- return;
+ goto out;
}
}
if (!bdrv_is_first_non_filter(state->old_bs)) {
error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
- return;
+ goto out;
}
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
if (node_name && !snapshot_node_name) {
error_setg(errp, "New snapshot node name missing");
- return;
+ 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");
- return;
+ goto out;
}
flags = state->old_bs->open_flags;
- flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ);
+ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_COPY_ON_READ);
+ flags |= BDRV_O_NO_BACKING;
/* create new image w/backing file */
mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
int64_t size = bdrv_getlength(state->old_bs);
if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed");
- return;
+ goto out;
}
bdrv_img_create(new_image_file, format,
state->old_bs->filename,
NULL, size, flags, false, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
}
qdict_put_str(options, "node-name", snapshot_node_name);
}
qdict_put_str(options, "driver", format);
-
- flags |= BDRV_O_NO_BACKING;
}
state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags,
errp);
/* We will manually add the backing_hd field to the bs later */
if (!state->new_bs) {
- return;
+ goto out;
}
if (bdrv_has_blk(state->new_bs)) {
error_setg(errp, "The snapshot is already in use");
- return;
+ goto out;
}
if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
errp)) {
- return;
+ goto out;
}
if (state->new_bs->backing != NULL) {
error_setg(errp, "The snapshot already has a backing image");
- return;
+ goto out;
}
if (!state->new_bs->drv->supports_backing) {
error_setg(errp, "The snapshot does not support backing images");
- return;
+ goto out;
}
- bdrv_set_aio_context(state->new_bs, state->aio_context);
+ bdrv_set_aio_context(state->new_bs, aio_context);
/* 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
bdrv_append(state->new_bs, state->old_bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
state->overlay_appended = true;
+
+out:
+ aio_context_release(aio_context);
}
static void external_snapshot_commit(BlkActionState *common)
{
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
+ AioContext *aio_context;
+
+ aio_context = bdrv_get_aio_context(state->old_bs);
+ aio_context_acquire(aio_context);
/* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
NULL);
}
+
+ aio_context_release(aio_context);
}
static void external_snapshot_abort(BlkActionState *common)
DO_UPCAST(ExternalSnapshotState, common, common);
if (state->new_bs) {
if (state->overlay_appended) {
+ AioContext *aio_context;
+
+ aio_context = bdrv_get_aio_context(state->old_bs);
+ aio_context_acquire(aio_context);
+
bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd()
close state->old_bs; we need it */
bdrv_set_backing_hd(state->new_bs, NULL, &error_abort);
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
+
+ aio_context_release(aio_context);
}
}
}
{
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
- if (state->aio_context) {
- bdrv_drained_end(state->old_bs);
- aio_context_release(state->aio_context);
- bdrv_unref(state->new_bs);
+ AioContext *aio_context;
+
+ if (!state->old_bs) {
+ return;
}
+
+ aio_context = bdrv_get_aio_context(state->old_bs);
+ aio_context_acquire(aio_context);
+
+ bdrv_drained_end(state->old_bs);
+ bdrv_unref(state->new_bs);
+
+ aio_context_release(aio_context);
}
typedef struct DriveBackupState {
BlkActionState common;
BlockDriverState *bs;
- AioContext *aio_context;
BlockJob *job;
} DriveBackupState;
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockDriverState *bs;
DriveBackup *backup;
+ AioContext *aio_context;
Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
return;
}
- /* AioContext is released in .clean() */
- state->aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(state->aio_context);
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
+ /* Paired with .clean() */
bdrv_drained_begin(bs);
+
state->bs = bs;
state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
+
+out:
+ aio_context_release(aio_context);
}
static void drive_backup_commit(BlkActionState *common)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
+ AioContext *aio_context;
+
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
assert(state->job);
block_job_start(state->job);
+
+ aio_context_release(aio_context);
}
static void drive_backup_abort(BlkActionState *common)
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
if (state->job) {
+ AioContext *aio_context;
+
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
block_job_cancel_sync(state->job);
+
+ aio_context_release(aio_context);
}
}
static void drive_backup_clean(BlkActionState *common)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
+ AioContext *aio_context;
- if (state->aio_context) {
- bdrv_drained_end(state->bs);
- aio_context_release(state->aio_context);
+ if (!state->bs) {
+ return;
}
+
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
+ bdrv_drained_end(state->bs);
+
+ aio_context_release(aio_context);
}
typedef struct BlockdevBackupState {
BlkActionState common;
BlockDriverState *bs;
BlockJob *job;
- AioContext *aio_context;
} BlockdevBackupState;
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
BlockdevBackup *backup;
BlockDriverState *bs, *target;
+ AioContext *aio_context;
Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
return;
}
- /* AioContext is released in .clean() */
- state->aio_context = bdrv_get_aio_context(bs);
- if (state->aio_context != bdrv_get_aio_context(target)) {
- state->aio_context = NULL;
+ 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(state->aio_context);
+ aio_context_acquire(aio_context);
state->bs = bs;
+
+ /* Paired with .clean() */
bdrv_drained_begin(state->bs);
state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
+
+out:
+ aio_context_release(aio_context);
}
static void blockdev_backup_commit(BlkActionState *common)
{
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ AioContext *aio_context;
+
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
assert(state->job);
block_job_start(state->job);
+
+ aio_context_release(aio_context);
}
static void blockdev_backup_abort(BlkActionState *common)
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
if (state->job) {
+ AioContext *aio_context;
+
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
block_job_cancel_sync(state->job);
+
+ aio_context_release(aio_context);
}
}
static void blockdev_backup_clean(BlkActionState *common)
{
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ AioContext *aio_context;
- if (state->aio_context) {
- bdrv_drained_end(state->bs);
- aio_context_release(state->aio_context);
+ if (!state->bs) {
+ return;
}
+
+ aio_context = bdrv_get_aio_context(state->bs);
+ aio_context_acquire(aio_context);
+
+ bdrv_drained_end(state->bs);
+
+ aio_context_release(aio_context);
}
typedef struct BlockDirtyBitmapState {
BlkActionState common;
BdrvDirtyBitmap *bitmap;
BlockDriverState *bs;
- AioContext *aio_context;
HBitmap *backup;
bool prepared;
} BlockDirtyBitmapState;
/* AIO context taken and released within qmp_block_dirty_bitmap_add */
qmp_block_dirty_bitmap_add(action->node, action->name,
action->has_granularity, action->granularity,
+ action->has_persistent, action->persistent,
+ action->has_autoload, action->autoload,
&local_err);
if (!local_err) {
} else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
error_setg(errp, "Cannot clear a disabled bitmap");
return;
+ } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
+ error_setg(errp, "Cannot clear a readonly bitmap");
+ return;
}
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
- /* AioContext is released in .clean() */
}
static void block_dirty_bitmap_clear_abort(BlkActionState *common)
hbitmap_free(state->backup);
}
-static void block_dirty_bitmap_clear_clean(BlkActionState *common)
-{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
-
- if (state->aio_context) {
- aio_context_release(state->aio_context);
- }
-}
-
static void abort_prepare(BlkActionState *common, Error **errp)
{
error_setg(errp, "Transaction aborted using Abort action");
.prepare = block_dirty_bitmap_clear_prepare,
.commit = block_dirty_bitmap_clear_commit,
.abort = block_dirty_bitmap_clear_abort,
- .clean = block_dirty_bitmap_clear_clean,
}
};
}
error_free(local_err);
- qmp_x_blockdev_remove_medium(has_device, device, has_id, id, errp);
+ blockdev_remove_medium(has_device, device, has_id, id, errp);
}
void qmp_block_passwd(bool has_device, const char *device,
}
}
-void qmp_x_blockdev_remove_medium(bool has_device, const char *device,
- bool has_id, const char *id, Error **errp)
+static void blockdev_remove_medium(bool has_device, const char *device,
+ bool has_id, const char *id, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
aio_context_release(aio_context);
}
+void qmp_blockdev_remove_medium(const char *id, Error **errp)
+{
+ blockdev_remove_medium(false, NULL, true, id, errp);
+}
+
static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
BlockDriverState *bs, Error **errp)
{
}
}
-void qmp_x_blockdev_insert_medium(bool has_device, const char *device,
- bool has_id, const char *id,
- const char *node_name, Error **errp)
+static void blockdev_insert_medium(bool has_device, const char *device,
+ bool has_id, const char *id,
+ const char *node_name, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
qmp_blockdev_insert_anon_medium(blk, bs, errp);
}
+void qmp_blockdev_insert_medium(const char *id, const char *node_name,
+ Error **errp)
+{
+ blockdev_insert_medium(false, NULL, true, id, node_name, errp);
+}
+
void qmp_blockdev_change_medium(bool has_device, const char *device,
bool has_id, const char *id,
const char *filename,
error_free(err);
err = NULL;
- qmp_x_blockdev_remove_medium(has_device, device, has_id, id, &err);
+ blockdev_remove_medium(has_device, device, has_id, id, &err);
if (err) {
error_propagate(errp, err);
goto fail;
if (throttle_enabled(&cfg)) {
/* Enable I/O limits if they're not enabled yet, otherwise
* just update the throttling group. */
- if (!blk_get_public(blk)->throttle_state) {
+ if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
blk_io_limits_enable(blk,
arg->has_group ? arg->group :
arg->has_device ? arg->device :
}
/* Set the new throttling configuration */
blk_set_io_limits(blk, &cfg);
- } else if (blk_get_public(blk)->throttle_state) {
+ } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
/* If all throttling settings are set to 0, disable I/O limits */
blk_io_limits_disable(blk);
}
void qmp_block_dirty_bitmap_add(const char *node, const char *name,
bool has_granularity, uint32_t granularity,
+ bool has_persistent, bool persistent,
+ bool has_autoload, bool autoload,
Error **errp)
{
BlockDriverState *bs;
+ BdrvDirtyBitmap *bitmap;
if (!name || name[0] == '\0') {
error_setg(errp, "Bitmap name cannot be empty");
granularity = bdrv_get_default_bitmap_granularity(bs);
}
- bdrv_create_dirty_bitmap(bs, granularity, name, errp);
+ if (!has_persistent) {
+ persistent = false;
+ }
+
+ if (has_autoload) {
+ warn_report("Autoload option is deprecated and its value is ignored");
+ }
+
+ if (persistent &&
+ !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
+ {
+ return;
+ }
+
+ bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
+ if (bitmap == NULL) {
+ return;
+ }
+
+ bdrv_dirty_bitmap_set_persistance(bitmap, persistent);
}
void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
{
BlockDriverState *bs;
BdrvDirtyBitmap *bitmap;
+ Error *local_err = NULL;
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
if (!bitmap || !bs) {
name);
return;
}
+
+ if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
+ bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
bdrv_dirty_bitmap_make_anon(bitmap);
bdrv_release_dirty_bitmap(bs, bitmap);
}
"Bitmap '%s' is currently disabled and cannot be cleared",
name);
return;
+ } else if (bdrv_dirty_bitmap_readonly(bitmap)) {
+ error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
+ return;
}
bdrv_clear_dirty_bitmap(bitmap, NULL);
}
+BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
+ const char *name,
+ Error **errp)
+{
+ BdrvDirtyBitmap *bitmap;
+ BlockDriverState *bs;
+ BlockDirtyBitmapSha256 *ret = NULL;
+ char *sha256;
+
+ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
+ if (!bitmap || !bs) {
+ return NULL;
+ }
+
+ sha256 = bdrv_dirty_bitmap_sha256(bitmap, errp);
+ if (sha256 == NULL) {
+ return NULL;
+ }
+
+ ret = g_new(BlockDirtyBitmapSha256, 1);
+ ret->sha256 = sha256;
+
+ return ret;
+}
+
void hmp_drive_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");
}
bdrv_drained_begin(bs);
- ret = blk_truncate(blk, size, errp);
+ ret = blk_truncate(blk, size, PREALLOC_MODE_OFF, errp);
bdrv_drained_end(bs);
out:
return;
}
+ /* Early check to avoid creating target */
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
+ return;
+ }
+
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
backing_mode = MIRROR_OPEN_BACKING_CHAIN;
}
+ /* Don't open backing image in create() */
+ flags |= BDRV_O_NO_BACKING;
+
if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
&& arg->mode != NEW_IMAGE_MODE_EXISTING)
{
/* Mirroring takes care of copy-on-write using the source's backing
* file.
*/
- target_bs = bdrv_open(arg->target, NULL, options,
- flags | BDRV_O_NO_BACKING, errp);
+ target_bs = bdrv_open(arg->target, NULL, options, flags, errp);
if (!target_bs) {
goto out;
}
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
QDict *qdict;
+ const QDictEntry *ent;
Error *local_err = NULL;
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
qdict_flatten(qdict);
+ /*
+ * Rewrite "backing": null to "backing": ""
+ * TODO Rewrite "" to null instead, and perhaps not even here
+ */
+ for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) {
+ char *dot = strrchr(ent->key, '.');
+
+ if (!strcmp(dot ? dot + 1 : ent->key, "backing")
+ && qobject_type(ent->value) == QTYPE_QNULL) {
+ qdict_put(qdict, ent->key, qstring_new());
+ }
+ }
+
if (!qdict_get_try_str(qdict, "node-name")) {
error_setg(errp, "'node-name' must be specified for the root node");
goto fail;
return head;
}
+void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
+ bool has_force, bool force, Error **errp)
+{
+ AioContext *old_context;
+ AioContext *new_context;
+ BlockDriverState *bs;
+
+ bs = bdrv_find_node(node_name);
+ if (!bs) {
+ error_setg(errp, "Cannot find node %s", node_name);
+ return;
+ }
+
+ /* Protects against accidents. */
+ if (!(has_force && force) && bdrv_has_blk(bs)) {
+ error_setg(errp, "Node %s is associated with a BlockBackend and could "
+ "be in use (use force=true to override this check)",
+ node_name);
+ return;
+ }
+
+ if (iothread->type == QTYPE_QSTRING) {
+ IOThread *obj = iothread_by_id(iothread->u.s);
+ if (!obj) {
+ error_setg(errp, "Cannot find iothread %s", iothread->u.s);
+ return;
+ }
+
+ new_context = iothread_get_aio_context(obj);
+ } else {
+ new_context = qemu_get_aio_context();
+ }
+
+ old_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(old_context);
+
+ bdrv_set_aio_context(bs, new_context);
+
+ aio_context_release(old_context);
+}
+
QemuOptsList qemu_common_drive_opts = {
.name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),