}
}
+static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
+ Error **errp)
+{
+ const QListEntry *entry;
+ for (entry = qlist_first(intervals); entry; entry = qlist_next(entry)) {
+ switch (qobject_type(entry->value)) {
+
+ case QTYPE_QSTRING: {
+ unsigned long long length;
+ const char *str = qstring_get_str(qobject_to_qstring(entry->value));
+ if (parse_uint_full(str, &length, 10) == 0 &&
+ length > 0 && length <= UINT_MAX) {
+ block_acct_add_interval(stats, (unsigned) length);
+ } else {
+ error_setg(errp, "Invalid interval length: %s", str);
+ return false;
+ }
+ break;
+ }
+
+ case QTYPE_QINT: {
+ int64_t length = qint_get_int(qobject_to_qint(entry->value));
+ if (length > 0 && length <= UINT_MAX) {
+ block_acct_add_interval(stats, (unsigned) length);
+ } else {
+ error_setg(errp, "Invalid interval length: %" PRId64, length);
+ return false;
+ }
+ break;
+ }
+
+ default:
+ error_setg(errp, "The specification of stats-intervals is invalid");
+ return false;
+ }
+ }
+ return true;
+}
+
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
{
if (throttle_conflicting(cfg)) {
}
}
- if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
- *bdrv_flags |= BDRV_O_CACHE_WB;
- }
- if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
- *bdrv_flags |= BDRV_O_NOCACHE;
- }
- if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
- *bdrv_flags |= BDRV_O_NO_FLUSH;
- }
-
if ((aio = qemu_opt_get(opts, "aio")) != NULL) {
if (!strcmp(aio, "native")) {
*bdrv_flags |= BDRV_O_NATIVE_AIO;
*detect_zeroes =
qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
qemu_opt_get(opts, "detect-zeroes"),
- BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
+ BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX,
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
&local_error);
if (local_error) {
int bdrv_flags = 0;
int on_read_error, on_write_error;
bool account_invalid, account_failed;
- const char *stats_intervals;
BlockBackend *blk;
BlockDriverState *bs;
ThrottleConfig cfg;
int snapshot = 0;
Error *error = NULL;
QemuOpts *opts;
+ QDict *interval_dict = NULL;
+ QList *interval_list = NULL;
const char *id;
- bool has_driver_specific_opts;
BlockdevDetectZeroesOptions detect_zeroes =
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
const char *throttling_group = NULL;
qdict_del(bs_opts, "id");
}
- has_driver_specific_opts = !!qdict_size(bs_opts);
-
/* extract parameters */
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true);
account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true);
- stats_intervals = qemu_opt_get(opts, "stats-intervals");
+ qdict_extract_subqdict(bs_opts, &interval_dict, "stats-intervals.");
+ qdict_array_split(interval_dict, &interval_list);
+
+ if (qdict_size(interval_dict) != 0) {
+ error_setg(errp, "Invalid option stats-intervals.%s",
+ qdict_first(interval_dict)->key);
+ goto early_err;
+ }
extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg,
&detect_zeroes, &error);
}
if (snapshot) {
- /* always use cache=unsafe with snapshot */
- bdrv_flags &= ~BDRV_O_CACHE_MASK;
- bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
+ bdrv_flags |= BDRV_O_SNAPSHOT;
}
/* init */
- if ((!file || !*file) && !has_driver_specific_opts) {
+ if ((!file || !*file) && !qdict_size(bs_opts)) {
BlockBackendRootState *blk_rs;
blk = blk_new(qemu_opts_id(opts), errp);
file = NULL;
}
+ /* 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");
+
+ if (snapshot) {
+ /* always use cache=unsafe with snapshot */
+ qdict_put(bs_opts, BDRV_OPT_CACHE_WB, qstring_from_str("on"));
+ qdict_put(bs_opts, BDRV_OPT_CACHE_DIRECT, qstring_from_str("off"));
+ qdict_put(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, qstring_from_str("on"));
+ }
+
blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,
errp);
if (!blk) {
block_acct_init(blk_get_stats(blk), account_invalid, account_failed);
- if (stats_intervals) {
- char **intervals = g_strsplit(stats_intervals, ":", 0);
- unsigned i;
-
- if (*stats_intervals == '\0') {
- error_setg(&error, "stats-intervals can't have an empty value");
- }
-
- for (i = 0; !error && intervals[i] != NULL; i++) {
- unsigned long long val;
- if (parse_uint_full(intervals[i], &val, 10) == 0 &&
- val > 0 && val <= UINT_MAX) {
- block_acct_add_interval(blk_get_stats(blk), val);
- } else {
- error_setg(&error, "Invalid interval length: '%s'",
- intervals[i]);
- }
- }
-
- g_strfreev(intervals);
-
- if (error) {
- error_propagate(errp, error);
- blk_unref(blk);
- blk = NULL;
- goto err_no_bs_opts;
- }
+ if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) {
+ blk_unref(blk);
+ blk = NULL;
+ goto err_no_bs_opts;
}
}
err_no_bs_opts:
qemu_opts_del(opts);
+ QDECREF(interval_dict);
+ QDECREF(interval_list);
return blk;
early_err:
qemu_opts_del(opts);
+ QDECREF(interval_dict);
+ QDECREF(interval_list);
err_no_opts:
QDECREF(bs_opts);
return NULL;
.prepare = external_snapshot_prepare,
.commit = external_snapshot_commit,
.abort = external_snapshot_abort,
+ .clean = external_snapshot_clean,
},
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
.instance_size = sizeof(ExternalSnapshotState),
return;
}
- qmp_blockdev_remove_medium(device, errp);
+ qmp_x_blockdev_remove_medium(device, errp);
}
void qmp_block_passwd(bool has_device, const char *device,
blk_dev_change_media_cb(blk, true);
}
-void qmp_blockdev_remove_medium(const char *device, Error **errp)
+void qmp_x_blockdev_remove_medium(const char *device, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list);
}
-void qmp_blockdev_insert_medium(const char *device, const char *node_name,
- Error **errp)
+void qmp_x_blockdev_insert_medium(const char *device, const char *node_name,
+ Error **errp)
{
BlockDriverState *bs;
goto fail;
}
- qmp_blockdev_remove_medium(device, &err);
+ qmp_x_blockdev_remove_medium(device, &err);
if (err) {
error_propagate(errp, err);
goto fail;
bmap = bdrv_find_dirty_bitmap(bs, bitmap);
if (!bmap) {
error_setg(errp, "Bitmap '%s' could not be found", bitmap);
+ bdrv_unref(target_bs);
goto out;
}
}
NULL, errp);
}
-void qmp_drive_mirror(const char *device, const char *target,
- bool has_format, const char *format,
- bool has_node_name, const char *node_name,
- bool has_replaces, const char *replaces,
- enum MirrorSyncMode sync,
- bool has_mode, enum NewImageMode mode,
- bool has_speed, int64_t speed,
- bool has_granularity, uint32_t granularity,
- bool has_buf_size, int64_t buf_size,
- bool has_on_source_error, BlockdevOnError on_source_error,
- bool has_on_target_error, BlockdevOnError on_target_error,
- bool has_unmap, bool unmap,
- Error **errp)
+/* Parameter check and block job starting for drive mirroring.
+ * Caller should hold @device and @target's aio context (must be the same).
+ **/
+static void blockdev_mirror_common(BlockDriverState *bs,
+ BlockDriverState *target,
+ bool has_replaces, const char *replaces,
+ enum MirrorSyncMode sync,
+ bool has_speed, int64_t speed,
+ bool has_granularity, uint32_t granularity,
+ bool has_buf_size, int64_t buf_size,
+ bool has_on_source_error,
+ BlockdevOnError on_source_error,
+ bool has_on_target_error,
+ BlockdevOnError on_target_error,
+ bool has_unmap, bool unmap,
+ Error **errp)
{
- BlockBackend *blk;
- BlockDriverState *bs;
- BlockDriverState *source, *target_bs;
- AioContext *aio_context;
- Error *local_err = NULL;
- QDict *options;
- int flags;
- int64_t size;
- int ret;
if (!has_speed) {
speed = 0;
if (!has_on_target_error) {
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
}
- if (!has_mode) {
- mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
- }
if (!has_granularity) {
granularity = 0;
}
return;
}
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
+ return;
+ }
+ 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;
+ }
+
+ /* 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
+ */
+ mirror_start(bs, target,
+ has_replaces ? replaces : NULL,
+ speed, granularity, buf_size, sync,
+ on_source_error, on_target_error, unmap,
+ block_job_cb, bs, errp);
+}
+
+void qmp_drive_mirror(const char *device, const char *target,
+ bool has_format, const char *format,
+ bool has_node_name, const char *node_name,
+ bool has_replaces, const char *replaces,
+ enum MirrorSyncMode sync,
+ bool has_mode, enum NewImageMode mode,
+ bool has_speed, int64_t speed,
+ bool has_granularity, uint32_t granularity,
+ bool has_buf_size, int64_t buf_size,
+ bool has_on_source_error, BlockdevOnError on_source_error,
+ bool has_on_target_error, BlockdevOnError on_target_error,
+ bool has_unmap, bool unmap,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ BlockBackend *blk;
+ BlockDriverState *source, *target_bs;
+ AioContext *aio_context;
+ Error *local_err = NULL;
+ QDict *options = NULL;
+ int flags;
+ int64_t size;
+ int ret;
+
blk = blk_by_name(device);
if (!blk) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
goto out;
}
bs = blk_bs(blk);
+ if (!has_mode) {
+ mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ }
if (!has_format) {
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
}
- if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
- goto out;
- }
-
flags = bs->open_flags | BDRV_O_RDWR;
source = backing_bs(bs);
if (!source && sync == MIRROR_SYNC_MODE_TOP) {
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
- */
- mirror_start(bs, target_bs,
- has_replaces ? replaces : NULL,
- speed, granularity, buf_size, sync,
- on_source_error, on_target_error,
- unmap,
- block_job_cb, bs, &local_err);
- if (local_err != NULL) {
+ blockdev_mirror_common(bs, target_bs,
+ has_replaces, replaces, sync,
+ has_speed, speed,
+ has_granularity, granularity,
+ has_buf_size, buf_size,
+ has_on_source_error, on_source_error,
+ has_on_target_error, on_target_error,
+ has_unmap, unmap,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
bdrv_unref(target_bs);
+ }
+out:
+ aio_context_release(aio_context);
+}
+
+void qmp_blockdev_mirror(const char *device, const char *target,
+ bool has_replaces, const char *replaces,
+ MirrorSyncMode sync,
+ bool has_speed, int64_t speed,
+ bool has_granularity, uint32_t granularity,
+ bool has_buf_size, int64_t buf_size,
+ bool has_on_source_error,
+ BlockdevOnError on_source_error,
+ bool has_on_target_error,
+ BlockdevOnError on_target_error,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ BlockBackend *blk;
+ BlockDriverState *target_bs;
+ AioContext *aio_context;
+ Error *local_err = NULL;
+
+ blk = blk_by_name(device);
+ if (!blk) {
+ error_setg(errp, "Device '%s' not found", device);
+ return;
+ }
+ bs = blk_bs(blk);
+
+ if (!bs) {
+ error_setg(errp, "Device '%s' has no media", device);
+ return;
+ }
+
+ target_bs = bdrv_lookup_bs(target, target, errp);
+ if (!target_bs) {
+ return;
+ }
+
+ 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,
+ has_replaces, replaces, sync,
+ has_speed, speed,
+ has_granularity, granularity,
+ has_buf_size, buf_size,
+ has_on_source_error, on_source_error,
+ has_on_target_error, on_target_error,
+ true, true,
+ &local_err);
+ if (local_err) {
error_propagate(errp, local_err);
- goto out;
+ bdrv_unref(target_bs);
}
-out:
aio_context_release(aio_context);
}
.name = "discard",
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
- },{
- .name = BDRV_OPT_CACHE_WB,
- .type = QEMU_OPT_BOOL,
- .help = "enables writeback mode for any caches",
- },{
- .name = BDRV_OPT_CACHE_DIRECT,
- .type = QEMU_OPT_BOOL,
- .help = "enables use of O_DIRECT (bypass the host page cache)",
- },{
- .name = BDRV_OPT_CACHE_NO_FLUSH,
- .type = QEMU_OPT_BOOL,
- .help = "ignore any flush requests for the device",
},{
.name = "aio",
.type = QEMU_OPT_STRING,
.type = QEMU_OPT_BOOL,
.help = "whether to account for failed I/O operations "
"in the statistics",
- },{
- .name = "stats-intervals",
- .type = QEMU_OPT_STRING,
- .help = "colon-separated list of intervals "
- "for collecting I/O statistics, in seconds",
},
{ /* end of list */ }
},
.name = "discard",
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
- },{
- .name = "cache.writeback",
- .type = QEMU_OPT_BOOL,
- .help = "enables writeback mode for any caches",
- },{
- .name = "cache.direct",
- .type = QEMU_OPT_BOOL,
- .help = "enables use of O_DIRECT (bypass the host page cache)",
- },{
- .name = "cache.no-flush",
- .type = QEMU_OPT_BOOL,
- .help = "ignore any flush requests for the device",
},{
.name = "aio",
.type = QEMU_OPT_STRING,