* Do not change these numbers! They govern how drive option
* index maps to unit and bus. That mapping is ABI.
*
- * All controllers used to imlement if=T drives need to support
+ * All controllers used to implement if=T drives need to support
* if_max_devs[T] units, for any T with if_max_devs[T] != 0.
* Otherwise, some index values map to "impossible" bus, unit
* values.
int bdrv_flags = 0;
int on_read_error, on_write_error;
bool account_invalid, account_failed;
+ bool writethrough;
BlockBackend *blk;
BlockDriverState *bs;
ThrottleConfig cfg;
account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true);
account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true);
+ writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true);
+
qdict_extract_subqdict(bs_opts, &interval_dict, "stats-intervals.");
qdict_array_split(interval_dict, &interval_list);
if ((!file || !*file) && !qdict_size(bs_opts)) {
BlockBackendRootState *blk_rs;
- blk = blk_new(errp);
- if (!blk) {
- goto early_err;
- }
-
+ blk = blk_new();
blk_rs = blk_get_root_state(blk);
blk_rs->open_flags = bdrv_flags;
blk_rs->read_only = !(bdrv_flags & BDRV_O_RDWR);
blk_rs->detect_zeroes = detect_zeroes;
- if (throttle_enabled(&cfg)) {
- if (!throttling_group) {
- throttling_group = blk_name(blk);
- }
- blk_rs->throttle_group = g_strdup(throttling_group);
- blk_rs->throttle_state = throttle_group_incref(throttling_group);
- blk_rs->throttle_state->cfg = cfg;
- }
-
QDECREF(bs_opts);
} else {
if (file && !*file) {
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
* with other callers) rather than what we want as the real defaults.
* Apply the defaults here instead. */
- qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
+ assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0);
if (runstate_check(RUN_STATE_INMIGRATE)) {
bdrv_flags |= BDRV_O_INACTIVE;
bs->detect_zeroes = detect_zeroes;
- /* disk I/O throttling */
- if (throttle_enabled(&cfg)) {
- if (!throttling_group) {
- throttling_group = blk_name(blk);
- }
- bdrv_io_limits_enable(bs, throttling_group);
- bdrv_set_io_limits(bs, &cfg);
- }
-
if (bdrv_key_required(bs)) {
autostart = 0;
}
}
}
+ /* disk I/O throttling */
+ if (throttle_enabled(&cfg)) {
+ if (!throttling_group) {
+ throttling_group = blk_name(blk);
+ }
+ blk_io_limits_enable(blk, throttling_group);
+ blk_set_io_limits(blk, &cfg);
+ }
+
+ blk_set_enable_write_cache(blk, !writethrough);
blk_set_on_error(blk, on_read_error, on_write_error);
if (!monitor_add_blk(blk, qemu_opts_id(opts), errp)) {
QemuOpts *opts;
Error *local_error = NULL;
BlockdevDetectZeroesOptions detect_zeroes;
- int ret;
int bdrv_flags = 0;
opts = qemu_opts_create(&qemu_root_bds_opts, NULL, 1, errp);
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
* with other callers) rather than what we want as the real defaults.
* Apply the defaults here instead. */
- qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
bdrv_flags |= BDRV_O_INACTIVE;
}
- bs = NULL;
- ret = bdrv_open(&bs, NULL, NULL, bs_opts, bdrv_flags, errp);
- if (ret < 0) {
+ bs = bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp);
+ if (!bs) {
goto fail_no_bs_opts;
}
value = qemu_opt_get(all_opts, "cache");
if (value) {
int flags = 0;
+ bool writethrough;
- if (bdrv_parse_cache_flags(value, &flags) != 0) {
+ if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) {
error_report("invalid cache option");
return NULL;
}
/* Specific options take precedence */
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) {
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB,
- !!(flags & BDRV_O_CACHE_WB), &error_abort);
+ !writethrough, &error_abort);
}
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) {
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT,
static void external_snapshot_prepare(BlkActionState *common,
Error **errp)
{
- int flags = 0, ret;
+ int flags = 0;
QDict *options = NULL;
Error *local_err = NULL;
/* Device and node name of the image to generate the snapshot from */
flags |= BDRV_O_NO_BACKING;
}
- /* There is no BB attached during bdrv_open(), so we can't set a
- * writethrough mode. bdrv_append() will swap the WCE setting so that the
- * backing file becomes unconditionally writeback (which is what backing
- * files should always be) and the new overlay gets the original setting. */
- flags |= BDRV_O_CACHE_WB;
-
- assert(state->new_bs == NULL);
- ret = bdrv_open(&state->new_bs, new_image_file, snapshot_ref, options,
- flags, errp);
+ 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 (ret != 0) {
+ if (!state->new_bs) {
return;
}
- if (state->new_bs->blk != NULL) {
+ if (bdrv_has_blk(state->new_bs)) {
error_setg(errp, "The snapshot is already in use by %s",
- blk_name(state->new_bs->blk));
+ bdrv_get_parent_name(state->new_bs));
return;
}
block_job_txn_unref(block_job_txn);
}
+static int do_open_tray(const char *device, bool force, Error **errp);
+
void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
{
Error *local_err = NULL;
+ int rc;
+
+ if (!has_force) {
+ force = false;
+ }
- qmp_blockdev_open_tray(device, has_force, force, &local_err);
+ rc = do_open_tray(device, force, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
+ if (rc == EINPROGRESS) {
+ error_setg(errp, "Device '%s' is locked and force was not specified, "
+ "wait for tray to open and try again", device);
+ return;
+ }
+
qmp_x_blockdev_remove_medium(device, errp);
}
aio_context_release(aio_context);
}
-void qmp_blockdev_open_tray(const char *device, bool has_force, bool force,
- Error **errp)
+/**
+ * returns -errno on fatal error, +errno for non-fatal situations.
+ * errp will always be set when the return code is negative.
+ * May return +ENOSYS if the device has no tray,
+ * or +EINPROGRESS if the tray is locked and the guest has been notified.
+ */
+static int do_open_tray(const char *device, bool force, Error **errp)
{
BlockBackend *blk;
bool locked;
- if (!has_force) {
- force = false;
- }
-
blk = blk_by_name(device);
if (!blk) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device);
- return;
+ return -ENODEV;
}
if (!blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device '%s' is not removable", device);
- return;
+ return -ENOTSUP;
}
if (!blk_dev_has_tray(blk)) {
/* Ignore this command on tray-less devices */
- return;
+ return ENOSYS;
}
if (blk_dev_is_tray_open(blk)) {
- return;
+ return 0;
}
locked = blk_dev_is_medium_locked(blk);
if (!locked || force) {
blk_dev_change_media_cb(blk, false);
}
+
+ if (locked && !force) {
+ return EINPROGRESS;
+ }
+
+ return 0;
+}
+
+void qmp_blockdev_open_tray(const char *device, bool has_force, bool force,
+ Error **errp)
+{
+ if (!has_force) {
+ force = false;
+ }
+ do_open_tray(device, force, errp);
}
void qmp_blockdev_close_tray(const char *device, Error **errp)
return;
}
- if (bs->blk) {
+ if (bdrv_has_blk(bs)) {
error_setg(errp, "Node '%s' is already in use by '%s'", node_name,
- blk_name(bs->blk));
+ bdrv_get_parent_name(bs));
return;
}
{
BlockBackend *blk;
BlockDriverState *medium_bs = NULL;
- int bdrv_flags, ret;
- bool writethrough;
+ int bdrv_flags;
QDict *options = NULL;
Error *err = NULL;
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
BDRV_O_PROTOCOL);
- /* Must open the image in writeback mode as long as no BlockBackend is
- * attached. The right mode can be set as the final step after changing the
- * medium. */
- writethrough = !(bdrv_flags & BDRV_O_CACHE_WB);
- bdrv_flags |= BDRV_O_CACHE_WB;
-
if (!has_read_only) {
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
}
qdict_put(options, "driver", qstring_from_str(format));
}
- assert(!medium_bs);
- ret = bdrv_open(&medium_bs, filename, NULL, options, bdrv_flags, errp);
- if (ret < 0) {
+ medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
+ if (!medium_bs) {
goto fail;
}
- blk_apply_root_state(blk, medium_bs);
-
bdrv_add_key(medium_bs, NULL, &err);
if (err) {
error_propagate(errp, err);
goto fail;
}
- bdrv_set_enable_write_cache(medium_bs, !writethrough);
+ blk_apply_root_state(blk, medium_bs);
qmp_blockdev_close_tray(device, errp);
if (throttle_enabled(&cfg)) {
/* Enable I/O limits if they're not enabled yet, otherwise
* just update the throttling group. */
- if (!bs->throttle_state) {
- bdrv_io_limits_enable(bs, has_group ? group : device);
+ if (!blk_get_public(blk)->throttle_state) {
+ blk_io_limits_enable(blk, has_group ? group : device);
} else if (has_group) {
- bdrv_io_limits_update_group(bs, group);
+ blk_io_limits_update_group(blk, group);
}
/* Set the new throttling configuration */
- bdrv_set_io_limits(bs, &cfg);
- } else if (bs->throttle_state) {
+ blk_set_io_limits(blk, &cfg);
+ } else if (blk_get_public(blk)->throttle_state) {
/* If all throttling settings are set to 0, disable I/O limits */
- bdrv_io_limits_disable(bs);
+ blk_io_limits_disable(blk);
}
out:
Error *local_err = NULL;
int flags;
int64_t size;
- int ret;
if (!has_speed) {
speed = 0;
goto out;
}
- flags = bs->open_flags | BDRV_O_CACHE_WB | BDRV_O_RDWR;
+ flags = bs->open_flags | BDRV_O_RDWR;
/* See if we have a backing HD we can use to create our new image
* on top of. */
qdict_put(options, "driver", qstring_from_str(format));
}
- target_bs = NULL;
- ret = bdrv_open(&target_bs, target, NULL, options, flags, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
+ target_bs = bdrv_open(target, NULL, options, flags, errp);
+ if (!target_bs) {
goto out;
}
backup_start(bs, target_bs, speed, sync, bmap,
on_source_error, on_target_error,
block_job_cb, bs, txn, &local_err);
+ bdrv_unref(target_bs);
if (local_err != NULL) {
- bdrv_unref(target_bs);
error_propagate(errp, local_err);
goto out;
}
}
target_bs = blk_bs(target_blk);
- bdrv_ref(target_bs);
bdrv_set_aio_context(target_bs, aio_context);
backup_start(bs, target_bs, speed, sync, NULL, on_source_error,
on_target_error, block_job_cb, bs, txn, &local_err);
if (local_err != NULL) {
- bdrv_unref(target_bs);
error_propagate(errp, local_err);
}
out:
if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) {
return;
}
- if (target->blk) {
- error_setg(errp, "Cannot mirror to an attached block device");
- return;
- }
if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) {
sync = MIRROR_SYNC_MODE_FULL;
QDict *options = NULL;
int flags;
int64_t size;
- int ret;
blk = blk_by_name(device);
if (!blk) {
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
}
- flags = bs->open_flags | BDRV_O_CACHE_WB | BDRV_O_RDWR;
+ flags = bs->open_flags | BDRV_O_RDWR;
source = backing_bs(bs);
if (!source && sync == MIRROR_SYNC_MODE_TOP) {
sync = MIRROR_SYNC_MODE_FULL;
/* Mirroring takes care of copy-on-write using the source's backing
* file.
*/
- target_bs = NULL;
- ret = bdrv_open(&target_bs, target, NULL, options,
- flags | BDRV_O_NO_BACKING, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
+ target_bs = bdrv_open(target, NULL, options, flags | BDRV_O_NO_BACKING,
+ errp);
+ if (!target_bs) {
goto out;
}
has_on_target_error, on_target_error,
has_unmap, unmap,
&local_err);
+ bdrv_unref(target_bs);
if (local_err) {
error_propagate(errp, local_err);
- bdrv_unref(target_bs);
}
out:
aio_context_release(aio_context);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- bdrv_ref(target_bs);
bdrv_set_aio_context(target_bs, aio_context);
blockdev_mirror_common(bs, target_bs,
&local_err);
if (local_err) {
error_propagate(errp, local_err);
- bdrv_unref(target_bs);
}
aio_context_release(aio_context);
error_setg(errp, "Cannot find block backend %s", id);
return;
}
+ if (blk_legacy_dinfo(blk)) {
+ error_setg(errp, "Deleting block backend added with drive-add"
+ " is not supported");
+ return;
+ }
if (blk_get_refcnt(blk) > 1) {
error_setg(errp, "Block backend %s is in use", id);
return;
bs = blk_bs(blk);
aio_context = blk_get_aio_context(blk);
} else {
+ blk = NULL;
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Cannot find node %s", node_name);
return;
}
- blk = bs->blk;
- if (blk) {
+ if (bdrv_has_blk(bs)) {
error_setg(errp, "Node %s is in use by %s",
- node_name, blk_name(blk));
+ node_name, bdrv_get_parent_name(bs));
return;
}
aio_context = bdrv_get_aio_context(bs);
aio_context_release(aio_context);
}
+static BdrvChild *bdrv_find_child(BlockDriverState *parent_bs,
+ const char *child_name)
+{
+ BdrvChild *child;
+
+ QLIST_FOREACH(child, &parent_bs->children, next) {
+ if (strcmp(child->name, child_name) == 0) {
+ return child;
+ }
+ }
+
+ return NULL;
+}
+
+void qmp_x_blockdev_change(const char *parent, bool has_child,
+ const char *child, bool has_node,
+ const char *node, Error **errp)
+{
+ BlockDriverState *parent_bs, *new_bs = NULL;
+ BdrvChild *p_child;
+
+ parent_bs = bdrv_lookup_bs(parent, parent, errp);
+ if (!parent_bs) {
+ return;
+ }
+
+ if (has_child == has_node) {
+ if (has_child) {
+ error_setg(errp, "The parameters child and node are in conflict");
+ } else {
+ error_setg(errp, "Either child or node must be specified");
+ }
+ return;
+ }
+
+ if (has_child) {
+ p_child = bdrv_find_child(parent_bs, child);
+ if (!p_child) {
+ error_setg(errp, "Node '%s' does not have child '%s'",
+ parent, child);
+ return;
+ }
+ bdrv_del_child(parent_bs, p_child, errp);
+ }
+
+ if (has_node) {
+ new_bs = bdrv_find_node(node);
+ if (!new_bs) {
+ error_setg(errp, "Node '%s' not found", node);
+ return;
+ }
+ bdrv_add_child(parent_bs, new_bs, errp);
+ }
+}
+
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
{
BlockJobInfoList *head = NULL, **p_next = &head;
BlockDriverState *bs;
+ BdrvNextIterator it;
- for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
.name = "aio",
.type = QEMU_OPT_STRING,
.help = "host AIO implementation (threads, native)",
+ },{
+ .name = BDRV_OPT_CACHE_WB,
+ .type = QEMU_OPT_BOOL,
+ .help = "Enable writeback mode",
},{
.name = "format",
.type = QEMU_OPT_STRING,