diff --git a/MAINTAINERS b/MAINTAINERS index 64491c800c..fd78a563a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4081,7 +4081,7 @@ F: block/rbd.c VHDX M: Jeff Cody L: qemu-block@nongnu.org -S: Supported +S: Odd Fixes F: block/vhdx* VDI diff --git a/block.c b/block.c index 8848e9a7ed..cf08e64add 100644 --- a/block.c +++ b/block.c @@ -1497,6 +1497,17 @@ static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child) } } +static void coroutine_fn GRAPH_RDLOCK bdrv_child_cb_resize(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + + if (child->role & BDRV_CHILD_FILTERED) { + /* Best effort, ignore errors. */ + bdrv_co_refresh_total_sectors(bs, bs->total_sectors); + bdrv_co_parent_cb_resize(bs); + } +} + static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base, const char *filename, bool backing_mask_protocol, @@ -1529,6 +1540,7 @@ const BdrvChildClass child_of_bds = { .detach = bdrv_child_cb_detach, .inactivate = bdrv_child_cb_inactivate, .change_aio_ctx = bdrv_child_cb_change_aio_ctx, + .resize = bdrv_child_cb_resize, .update_filename = bdrv_child_cb_update_filename, .get_parent_aio_context = child_of_bds_get_parent_aio_context, }; diff --git a/block/accounting.c b/block/accounting.c index 3e46159569..0933c61f3a 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -28,6 +28,7 @@ #include "block/block_int.h" #include "qemu/timer.h" #include "system/qtest.h" +#include "qapi/error.h" static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; @@ -56,13 +57,25 @@ static bool bool_from_onoffauto(OnOffAuto val, bool def) } } -void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, - enum OnOffAuto account_failed) +bool block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, + enum OnOffAuto account_failed, uint32_t *stats_intervals, + uint32_t num_stats_intervals, Error **errp) { stats->account_invalid = bool_from_onoffauto(account_invalid, stats->account_invalid); stats->account_failed = bool_from_onoffauto(account_failed, stats->account_failed); + if (stats_intervals) { + for (int i = 0; i < num_stats_intervals; i++) { + if (stats_intervals[i] <= 0) { + error_setg(errp, "Invalid interval length: %u", stats_intervals[i]); + return false; + } + block_acct_add_interval(stats, stats_intervals[i]); + } + g_free(stats_intervals); + } + return true; } void block_acct_cleanup(BlockAcctStats *stats) diff --git a/block/crypto.c b/block/crypto.c index d4226cc68a..7c37b23e36 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -67,11 +67,18 @@ static int block_crypto_read_func(QCryptoBlock *block, BlockCrypto *crypto = bs->opaque; ssize_t ret; - GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (qemu_in_coroutine()) { + GRAPH_RDLOCK_GUARD(); - ret = bdrv_pread(crypto->header ? crypto->header : bs->file, - offset, buflen, buf, 0); + ret = bdrv_co_pread(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); + } else { + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + ret = bdrv_pread(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); + } if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return ret; @@ -90,11 +97,18 @@ static int block_crypto_write_func(QCryptoBlock *block, BlockCrypto *crypto = bs->opaque; ssize_t ret; - GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (qemu_in_coroutine()) { + GRAPH_RDLOCK_GUARD(); - ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file, - offset, buflen, buf, 0); + ret = bdrv_co_pwrite(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); + } else { + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); + } if (ret < 0) { error_setg_errno(errp, -ret, "Could not write encryption header"); return ret; @@ -792,7 +806,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename, char *buf = NULL; int64_t size; bool detached_hdr = - qemu_opt_get_bool(opts, "detached-header", false); + qemu_opt_get_bool_del(opts, "detached-header", false); unsigned int cflags = 0; int ret; Error *local_err = NULL; diff --git a/block/curl.c b/block/curl.c index 68cf83ce55..d7d93d967f 100644 --- a/block/curl.c +++ b/block/curl.c @@ -524,7 +524,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) #endif #ifdef DEBUG_VERBOSE - if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) { + if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1L)) { goto err; } #endif diff --git a/block/io.c b/block/io.c index 9bd8ba8431..928c02d1ad 100644 --- a/block/io.c +++ b/block/io.c @@ -46,9 +46,6 @@ /* Maximum read size for checking if data reads as zero, in bytes */ #define MAX_ZERO_CHECK_BUFFER (128 * KiB) -static void coroutine_fn GRAPH_RDLOCK -bdrv_parent_cb_resize(BlockDriverState *bs); - static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestFlags flags); @@ -2038,7 +2035,7 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, end_sector > bs->total_sectors) && req->type != BDRV_TRACKED_DISCARD) { bs->total_sectors = end_sector; - bdrv_parent_cb_resize(bs); + bdrv_co_parent_cb_resize(bs); bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); } if (req->bytes) { @@ -3570,11 +3567,11 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, bytes, read_flags, write_flags); } -static void coroutine_fn GRAPH_RDLOCK -bdrv_parent_cb_resize(BlockDriverState *bs) +void coroutine_fn bdrv_co_parent_cb_resize(BlockDriverState *bs) { BdrvChild *c; + IO_CODE(); assert_bdrv_graph_readable(); QLIST_FOREACH(c, &bs->parents, next_parent) { diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 282d1c386e..3640d1f3dc 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -62,7 +62,7 @@ static void hmp_drive_add_node(Monitor *mon, const char *optstr) { QemuOpts *opts; QDict *qdict; - Error *local_err = NULL; + Error *err = NULL; opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false); if (!opts) { @@ -73,19 +73,19 @@ static void hmp_drive_add_node(Monitor *mon, const char *optstr) if (!qdict_get_try_str(qdict, "node-name")) { qobject_unref(qdict); - error_report("'node-name' needs to be specified"); + error_setg(&err, "'node-name' needs to be specified"); goto out; } - BlockDriverState *bs = bds_tree_init(qdict, &local_err); + BlockDriverState *bs = bds_tree_init(qdict, &err); if (!bs) { - error_report_err(local_err); goto out; } bdrv_set_monitor_owned(bs); out: qemu_opts_del(opts); + hmp_handle_error(mon, err); } void hmp_drive_add(Monitor *mon, const QDict *qdict) @@ -109,7 +109,6 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) mc = MACHINE_GET_CLASS(current_machine); dinfo = drive_new(opts, mc->block_default_type, &err); if (err) { - error_report_err(err); qemu_opts_del(opts); goto err; } @@ -123,7 +122,7 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) monitor_printf(mon, "OK\n"); break; default: - monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); + error_setg(&err, "Can't hot-add drive to type %d", dinfo->type); goto err; } return; @@ -134,6 +133,7 @@ err: monitor_remove_blk(blk); blk_unref(blk); } + hmp_handle_error(mon, err); } void hmp_drive_del(Monitor *mon, const QDict *qdict) @@ -141,36 +141,32 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) const char *id = qdict_get_str(qdict, "id"); BlockBackend *blk; BlockDriverState *bs; - Error *local_err = NULL; + Error *err = NULL; GLOBAL_STATE_CODE(); bdrv_graph_rdlock_main_loop(); bs = bdrv_find_node(id); if (bs) { - qmp_blockdev_del(id, &local_err); - if (local_err) { - error_report_err(local_err); - } + qmp_blockdev_del(id, &err); goto unlock; } blk = blk_by_name(id); if (!blk) { - error_report("Device '%s' not found", id); + error_setg(&err, "Device '%s' not found", id); goto unlock; } if (!blk_legacy_dinfo(blk)) { - error_report("Deleting device added with blockdev-add" - " is not supported"); + error_setg(&err, "Deleting device added with blockdev-add" + " is not supported"); goto unlock; } bs = blk_bs(blk); if (bs) { - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { - error_report_err(local_err); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &err)) { goto unlock; } @@ -196,6 +192,7 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) unlock: bdrv_graph_rdunlock_main_loop(); + hmp_handle_error(mon, err); } void hmp_commit(Monitor *mon, const QDict *qdict) @@ -203,6 +200,7 @@ void hmp_commit(Monitor *mon, const QDict *qdict) const char *device = qdict_get_str(qdict, "device"); BlockBackend *blk; int ret; + Error *err = NULL; GLOBAL_STATE_CODE(); GRAPH_RDLOCK_GUARD_MAINLOOP(); @@ -214,22 +212,25 @@ void hmp_commit(Monitor *mon, const QDict *qdict) blk = blk_by_name(device); if (!blk) { - error_report("Device '%s' not found", device); - return; + error_setg(&err, "Device '%s' not found", device); + goto end; } bs = bdrv_skip_implicit_filters(blk_bs(blk)); if (!blk_is_available(blk)) { - error_report("Device '%s' has no medium", device); - return; + error_setg(&err, "Device '%s' has no medium", device); + goto end; } ret = bdrv_commit(bs); } if (ret < 0) { - error_report("'commit' error for '%s': %s", device, strerror(-ret)); + error_setg(&err, "'commit' error for '%s': %s", device, strerror(-ret)); } + +end: + hmp_handle_error(mon, err); } void hmp_drive_mirror(Monitor *mon, const QDict *qdict) @@ -890,7 +891,7 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, &err); if (!bs) { - error_report_err(err); + hmp_handle_error(mon, err); return; } diff --git a/block/qapi.c b/block/qapi.c index 12fbf8d1b7..9f5771e019 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -235,7 +235,8 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, * in @info, setting @errp on error. */ static void GRAPH_RDLOCK -bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) +bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, bool limits, + Error **errp) { int64_t size; const char *backing_filename; @@ -269,6 +270,33 @@ bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) info->dirty_flag = bdi.is_dirty; info->has_dirty_flag = true; } + + if (limits) { + info->limits = g_new(BlockLimitsInfo, 1); + *info->limits = (BlockLimitsInfo) { + .request_alignment = bs->bl.request_alignment, + .has_max_discard = bs->bl.max_pdiscard != 0, + .max_discard = bs->bl.max_pdiscard, + .has_discard_alignment = bs->bl.pdiscard_alignment != 0, + .discard_alignment = bs->bl.pdiscard_alignment, + .has_max_write_zeroes = bs->bl.max_pwrite_zeroes != 0, + .max_write_zeroes = bs->bl.max_pwrite_zeroes, + .has_write_zeroes_alignment = bs->bl.pwrite_zeroes_alignment != 0, + .write_zeroes_alignment = bs->bl.pwrite_zeroes_alignment, + .has_opt_transfer = bs->bl.opt_transfer != 0, + .opt_transfer = bs->bl.opt_transfer, + .has_max_transfer = bs->bl.max_transfer != 0, + .max_transfer = bs->bl.max_transfer, + .has_max_hw_transfer = bs->bl.max_hw_transfer != 0, + .max_hw_transfer = bs->bl.max_hw_transfer, + .max_iov = bs->bl.max_iov, + .has_max_hw_iov = bs->bl.max_hw_iov != 0, + .max_hw_iov = bs->bl.max_hw_iov, + .min_mem_alignment = bs->bl.min_mem_alignment, + .opt_mem_alignment = bs->bl.opt_mem_alignment, + }; + } + info->format_specific = bdrv_get_specific_info(bs, &err); if (err) { error_propagate(errp, err); @@ -343,7 +371,7 @@ void bdrv_query_image_info(BlockDriverState *bs, ImageInfo *info; info = g_new0(ImageInfo, 1); - bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp); + bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), true, errp); if (*errp) { goto fail; } @@ -389,6 +417,7 @@ fail: */ void bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info, + bool limits, Error **errp) { ERRP_GUARD(); @@ -397,7 +426,7 @@ void bdrv_query_block_graph_info(BlockDriverState *bs, BdrvChild *c; info = g_new0(BlockGraphInfo, 1); - bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp); + bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), limits, errp); if (*errp) { goto fail; } @@ -411,7 +440,7 @@ void bdrv_query_block_graph_info(BlockDriverState *bs, QAPI_LIST_APPEND(children_list_tail, c_info); c_info->name = g_strdup(c->name); - bdrv_query_block_graph_info(c->bs, &c_info->info, errp); + bdrv_query_block_graph_info(c->bs, &c_info->info, limits, errp); if (*errp) { goto fail; } @@ -908,6 +937,29 @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, visit_free(v); } +/** + * Dumps the given BlockLimitsInfo object in a human-readable form, + * prepending an optional prefix if the dump is not empty. + */ +static void bdrv_image_info_limits_dump(BlockLimitsInfo *limits, + const char *prefix, + int indentation) +{ + QObject *obj; + Visitor *v = qobject_output_visitor_new(&obj); + + visit_type_BlockLimitsInfo(v, NULL, &limits, &error_abort); + visit_complete(v, &obj); + if (!qobject_is_empty_dump(obj)) { + if (prefix) { + qemu_printf("%*s%s", indentation * 4, "", prefix); + } + dump_qobject(indentation + 1, obj); + } + qobject_unref(obj); + visit_free(v); +} + /** * Print the given @info object in human-readable form. Every field is indented * using the given @indentation (four spaces per indentation level). @@ -983,6 +1035,12 @@ void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol) } } + if (info->limits) { + bdrv_image_info_limits_dump(info->limits, + "Block limits:\n", + indentation); + } + if (info->has_snapshots) { SnapshotInfoList *elem; diff --git a/blockdev.c b/blockdev.c index b451fee6e1..dbd1d4d3e8 100644 --- a/blockdev.c +++ b/blockdev.c @@ -617,7 +617,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, bs->detect_zeroes = detect_zeroes; - block_acct_setup(blk_get_stats(blk), account_invalid, account_failed); + block_acct_setup(blk_get_stats(blk), account_invalid, account_failed, + NULL, 0, NULL); if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) { blk_unref(blk); diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index 5e7b85079d..558b0eb84d 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -503,7 +503,7 @@ Command description: The size syntax is similar to :manpage:`dd(1)`'s size syntax. -.. option:: info [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [--backing-chain] [-U] FILENAME +.. option:: info [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [--backing-chain] [--limits] [-t CACHE] [-U] FILENAME Give information about the disk image *FILENAME*. Use it in particular to know the size reserved on disk which can be different @@ -571,6 +571,10 @@ Command description: ``ImageInfoSpecific*`` QAPI object (e.g. ``ImageInfoSpecificQCow2`` for qcow2 images). + *Block limits* + The block limits for I/O that QEMU detected for the image. + This information is only shown if the ``--limits`` option was specified. + .. option:: map [--object OBJECTDEF] [--image-opts] [-f FMT] [--start-offset=OFFSET] [--max-length=LEN] [--output=OFMT] [-U] FILENAME Dump the metadata of image *FILENAME* and its backing file chain. diff --git a/hw/block/block.c b/hw/block/block.c index 2e10611d95..f187fa025d 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -249,8 +249,11 @@ bool blkconf_apply_backend_options(BlockConf *conf, bool readonly, blk_set_enable_write_cache(blk, wce); blk_set_on_error(blk, rerror, werror); - block_acct_setup(blk_get_stats(blk), conf->account_invalid, - conf->account_failed); + if (!block_acct_setup(blk_get_stats(blk), conf->account_invalid, + conf->account_failed, conf->stats_intervals, + conf->num_stats_intervals, errp)) { + return false; + } return true; } diff --git a/include/block/accounting.h b/include/block/accounting.h index a59e39f49d..b1cf417b57 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -101,8 +101,9 @@ typedef struct BlockAcctCookie { } BlockAcctCookie; void block_acct_init(BlockAcctStats *stats); -void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, - enum OnOffAuto account_failed); +bool block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, + enum OnOffAuto account_failed, uint32_t *stats_intervals, + uint32_t num_stats_intervals, Error **errp); void block_acct_cleanup(BlockAcctStats *stats); void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length); BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 034c0634c8..f2a4e863fc 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -817,10 +817,10 @@ typedef struct BlockLimits { int64_t max_pdiscard; /* - * Optimal alignment for discard requests in bytes. A power of 2 - * is best but not mandatory. Must be a multiple of - * bl.request_alignment, and must be less than max_pdiscard if - * that is set. May be 0 if bl.request_alignment is good enough + * Optimal alignment for discard requests in bytes. Note that this doesn't + * have to be a power of two. Must be a multiple of bl.request_alignment, + * and must be less than max_pdiscard if that is set. May be 0 if + * bl.request_alignment is good enough. */ uint32_t pdiscard_alignment; @@ -831,11 +831,10 @@ typedef struct BlockLimits { int64_t max_pwrite_zeroes; /* - * Optimal alignment for write zeroes requests in bytes. A power - * of 2 is best but not mandatory. Must be a multiple of - * bl.request_alignment, and must be less than max_pwrite_zeroes - * if that is set. May be 0 if bl.request_alignment is good - * enough + * Optimal alignment for write zeroes requests in bytes. Note that this + * doesn't have to be a power of two. Must be a multiple of + * bl.request_alignment, and must be less than max_pwrite_zeroes if that is + * set. May be 0 if bl.request_alignment is good enough. */ uint32_t pwrite_zeroes_alignment; @@ -863,18 +862,23 @@ typedef struct BlockLimits { uint64_t max_hw_transfer; /* - * Maximal number of scatter/gather elements allowed by the hardware. + * Maximum number of scatter/gather elements allowed by the hardware. * Applies whenever transfers to the device bypass the kernel I/O * scheduler, for example with SG_IO. If larger than max_iov * or if zero, blk_get_max_hw_iov will fall back to max_iov. */ int max_hw_iov; - - /* memory alignment, in bytes so that no bounce buffer is needed */ + /* + * Minimal required memory alignment in bytes for zero-copy I/O to succeed. + * For unaligned requests, a bounce buffer will be used. + */ size_t min_mem_alignment; - /* memory alignment, in bytes, for bounce buffer */ + /* + * Optimal memory alignment in bytes. This is the alignment used for any + * buffer allocations QEMU performs internally. + */ size_t opt_mem_alignment; /* maximum number of iovec elements */ @@ -1020,7 +1024,10 @@ struct BdrvChildClass { * the I/O API. */ - void (*resize)(BdrvChild *child); + /* + * Notifies the parent that the child was resized. + */ + void GRAPH_RDLOCK_PTR (*resize)(BdrvChild *child); /* * Returns a name that is supposedly more useful for human users than the diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 4f94eb3c5a..ed8b5657d6 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -191,4 +191,10 @@ void bdrv_bsc_invalidate_range(BlockDriverState *bs, */ void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes); +/* + * Notify all parents that the size of the child changed. + */ +void coroutine_fn GRAPH_RDLOCK +bdrv_co_parent_cb_resize(BlockDriverState *bs); + #endif /* BLOCK_INT_IO_H */ diff --git a/include/block/qapi.h b/include/block/qapi.h index 54c48de26a..be554e53dc 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -42,7 +42,7 @@ bdrv_query_image_info(BlockDriverState *bs, ImageInfo **p_info, bool flat, bool skip_implicit_filters, Error **errp); void GRAPH_RDLOCK bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info, - Error **errp); + bool limits, Error **errp); void bdrv_snapshot_dump(QEMUSnapshotInfo *sn); void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, diff --git a/include/hw/block/block.h b/include/hw/block/block.h index de3946a5f1..b4d914624e 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -34,6 +34,8 @@ typedef struct BlockConf { OnOffAuto account_invalid, account_failed; BlockdevOnError rerror; BlockdevOnError werror; + uint32_t num_stats_intervals; + uint32_t *stats_intervals; } BlockConf; static inline unsigned int get_physical_block_exp(BlockConf *conf) @@ -66,7 +68,10 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) DEFINE_PROP_ON_OFF_AUTO("account-invalid", _state, \ _conf.account_invalid, ON_OFF_AUTO_AUTO), \ DEFINE_PROP_ON_OFF_AUTO("account-failed", _state, \ - _conf.account_failed, ON_OFF_AUTO_AUTO) + _conf.account_failed, ON_OFF_AUTO_AUTO), \ + DEFINE_PROP_ARRAY("stats-intervals", _state, \ + _conf.num_stats_intervals, _conf.stats_intervals, \ + qdev_prop_uint32, uint32_t) #define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \ diff --git a/qapi/block-core.json b/qapi/block-core.json index dc6eb4ae23..2c037183f0 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -275,6 +275,69 @@ 'file': 'ImageInfoSpecificFileWrapper' } } +## +# @BlockLimitsInfo: +# +# @request-alignment: Alignment requirement, in bytes, for +# offset/length of I/O requests. +# +# @max-discard: Maximum number of bytes that can be discarded at once. +# If not present, there is no specific maximum. +# +# @discard-alignment: Optimal alignment for discard requests in bytes. +# Note that this doesn't have to be a power of two. If not +# present, discards don't have a alignment requirement different +# from @request-alignment. +# +# @max-write-zeroes: Maximum number of bytes that can be zeroed out at +# once. If not present, there is no specific maximum. +# +# @write-zeroes-alignment: Optimal alignment for write zeroes requests +# in bytes. Note that this doesn't have to be a power of two. If +# not present, write_zeroes doesn't have a alignment requirement +# different from @request-alignment. +# +# @opt-transfer: Optimal transfer length in bytes. If not present, +# there is no preferred size. +# +# @max-transfer: Maximal transfer length in bytes. If not present, +# there is no specific maximum. +# +# @max-hw-transfer: Maximal hardware transfer length in bytes. +# Applies whenever transfers to the device bypass the kernel I/O +# scheduler, for example with SG_IO. If not present, there is no +# specific maximum. +# +# @max-iov: Maximum number of scatter/gather elements +# +# @max-hw-iov: Maximum number of scatter/gather elements allowed by +# the hardware. Applies whenever transfers to the device bypass +# the kernel I/O scheduler, for example with SG_IO. If not +# present, the hardware limits is unknown and @max-iov is always +# used. +# +# @min-mem-alignment: Minimal required memory alignment in bytes for +# zero-copy I/O to succeed. For unaligned requests, a bounce +# buffer will be used. +# +# @opt-mem-alignment: Optimal memory alignment in bytes. This is the +# alignment used for any buffer allocations QEMU performs +# internally. +## +{ 'struct': 'BlockLimitsInfo', + 'data': { 'request-alignment': 'uint32', + '*max-discard': 'uint64', + '*discard-alignment': 'uint32', + '*max-write-zeroes': 'uint64', + '*write-zeroes-alignment': 'uint32', + '*opt-transfer': 'uint32', + '*max-transfer': 'uint32', + '*max-hw-transfer': 'uint32', + 'max-iov': 'int', + '*max-hw-iov': 'int', + 'min-mem-alignment': 'size', + 'opt-mem-alignment': 'size' } } + ## # @BlockNodeInfo: # @@ -304,6 +367,8 @@ # # @snapshots: list of VM snapshots # +# @limits: block limits that are used for I/O on the node (Since 10.2) +# # @format-specific: structure supplying additional format-specific # information (since 1.7) # @@ -315,6 +380,7 @@ '*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool', '*backing-filename': 'str', '*full-backing-filename': 'str', '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'], + '*limits': 'BlockLimitsInfo', '*format-specific': 'ImageInfoSpecific' } } ## diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 2c5a8a28f9..6bc8265cfb 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -66,9 +66,9 @@ SRST ERST DEF("info", img_info, - "info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [-U] filename") + "info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [--limits] [-t CACHE] [-U] filename") SRST -.. option:: info [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [--backing-chain] [-U] FILENAME +.. option:: info [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [--backing-chain] [--limits] [-t CACHE] [-U] FILENAME ERST DEF("map", img_map, diff --git a/qemu-img.c b/qemu-img.c index 7a162fdc08..a7791896c1 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -86,6 +86,7 @@ enum { OPTION_BITMAPS = 275, OPTION_FORCE = 276, OPTION_SKIP_BROKEN = 277, + OPTION_LIMITS = 278, }; typedef enum OutputFormat { @@ -3002,12 +3003,23 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b) static BlockGraphInfoList *collect_image_info_list(bool image_opts, const char *filename, const char *fmt, - bool chain, bool force_share) + const char *cache, + bool chain, bool limits, + bool force_share) { BlockGraphInfoList *head = NULL; BlockGraphInfoList **tail = &head; GHashTable *filenames; Error *err = NULL; + int cache_flags = 0; + bool writethrough = false; + int ret; + + ret = bdrv_parse_cache_mode(cache, &cache_flags, &writethrough); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return NULL; + } filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL); @@ -3024,8 +3036,8 @@ static BlockGraphInfoList *collect_image_info_list(bool image_opts, g_hash_table_insert(filenames, (gpointer)filename, NULL); blk = img_open(image_opts, filename, fmt, - BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false, - force_share); + BDRV_O_NO_BACKING | BDRV_O_NO_IO | cache_flags, + writethrough, false, force_share); if (!blk) { goto err; } @@ -3039,7 +3051,7 @@ static BlockGraphInfoList *collect_image_info_list(bool image_opts, * the chain manually here. */ bdrv_graph_rdlock_main_loop(); - bdrv_query_block_graph_info(bs, &info, &err); + bdrv_query_block_graph_info(bs, &info, limits, &err); bdrv_graph_rdunlock_main_loop(); if (err) { @@ -3085,9 +3097,11 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) OutputFormat output_format = OFORMAT_HUMAN; bool chain = false; const char *filename, *fmt; + const char *cache = BDRV_DEFAULT_CACHE; BlockGraphInfoList *list; bool image_opts = false; bool force_share = false; + bool limits = false; fmt = NULL; for(;;) { @@ -3096,12 +3110,14 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, + {"cache", required_argument, 0, 't'}, {"force-share", no_argument, 0, 'U'}, + {"limits", no_argument, 0, OPTION_LIMITS}, {"output", required_argument, 0, OPTION_OUTPUT}, {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "hf:U", long_options, NULL); + c = getopt_long(argc, argv, "hf:t:U", long_options, NULL); if (c == -1) { break; } @@ -3117,8 +3133,12 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) " (incompatible with -f|--format)\n" " --backing-chain\n" " display information about the backing chain for copy-on-write overlays\n" +" -t, --cache CACHE\n" +" cache mode for FILE (default: " BDRV_DEFAULT_CACHE ")\n" " -U, --force-share\n" " open image in shared mode for concurrent access\n" +" --limits\n" +" show detected block limits (may depend on options, e.g. cache mode)\n" " --output human|json\n" " specify output format (default: human)\n" " --object OBJDEF\n" @@ -3137,9 +3157,15 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) case OPTION_BACKING_CHAIN: chain = true; break; + case 't': + cache = optarg; + break; case 'U': force_share = true; break; + case OPTION_LIMITS: + limits = true; + break; case OPTION_OUTPUT: output_format = parse_output_format(argv[0], optarg); break; @@ -3155,8 +3181,8 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) } filename = argv[optind++]; - list = collect_image_info_list(image_opts, filename, fmt, chain, - force_share); + list = collect_image_info_list(image_opts, filename, fmt, cache, chain, + limits, force_share); if (!list) { return 1; } diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out index 97f894cf8f..9178474e79 100644 --- a/tests/qemu-iotests/094.out +++ b/tests/qemu-iotests/094.out @@ -23,6 +23,6 @@ Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864 {'execute': 'quit'} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/119.out b/tests/qemu-iotests/119.out index 7b7f0f4bcc..45f82a4faa 100644 --- a/tests/qemu-iotests/119.out +++ b/tests/qemu-iotests/119.out @@ -5,7 +5,7 @@ QMP_VERSION read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out index 146fc72388..a023cd407d 100644 --- a/tests/qemu-iotests/172.out +++ b/tests/qemu-iotests/172.out @@ -30,6 +30,7 @@ Testing: share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" @@ -59,6 +60,7 @@ Testing: -fda TEST_DIR/t.qcow2 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -95,6 +97,7 @@ Testing: -fdb TEST_DIR/t.qcow2 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -109,6 +112,7 @@ Testing: -fdb TEST_DIR/t.qcow2 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -149,6 +153,7 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -163,6 +168,7 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -204,6 +210,7 @@ Testing: -fdb share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" dev: floppy, id "" unit = 0 (0x0) @@ -218,6 +225,7 @@ Testing: -fdb share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" @@ -247,6 +255,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -283,6 +292,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -297,6 +307,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -337,6 +348,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -351,6 +363,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -395,6 +408,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -431,6 +445,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -467,6 +482,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -481,6 +497,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -531,6 +548,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -545,6 +563,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -586,6 +605,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -600,6 +620,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -641,6 +662,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 1 (0x1) @@ -655,6 +677,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -696,6 +719,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 1 (0x1) @@ -710,6 +734,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -760,6 +785,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -774,6 +800,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -815,6 +842,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -829,6 +857,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -876,6 +905,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global floppy.drive=none0 -device share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -942,6 +972,7 @@ Testing: -device floppy share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" Testing: -device floppy,drive-type=120 @@ -968,6 +999,7 @@ Testing: -device floppy,drive-type=120 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "120" Testing: -device floppy,drive-type=144 @@ -994,6 +1026,7 @@ Testing: -device floppy,drive-type=144 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" Testing: -device floppy,drive-type=288 @@ -1020,6 +1053,7 @@ Testing: -device floppy,drive-type=288 share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" @@ -1049,6 +1083,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "120" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -1085,6 +1120,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "288" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -1124,6 +1160,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -1160,6 +1197,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica share-rw = false account-invalid = "auto" account-failed = "auto" + stats-intervals = drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184 index e4cbcd8634..6d0afe9d38 100755 --- a/tests/qemu-iotests/184 +++ b/tests/qemu-iotests/184 @@ -45,8 +45,9 @@ do_run_qemu() run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ - | _filter_qemu_io | _filter_generated_node_ids + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp \ + | _filter_qemu_io | _filter_generated_node_ids \ + | _filter_img_info } test_throttle=$($QEMU_IMG --help|grep throttle) diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out index ef99bb2e9a..52692b6b3b 100644 --- a/tests/qemu-iotests/184.out +++ b/tests/qemu-iotests/184.out @@ -41,12 +41,6 @@ Testing: }, "iops_wr": 0, "ro": false, - "children": [ - { - "node-name": "disk0", - "child": "file" - } - ], "node-name": "throttle0", "backing_file_depth": 1, "drv": "throttle", @@ -75,8 +69,6 @@ Testing: }, "iops_wr": 0, "ro": false, - "children": [ - ], "node-name": "disk0", "backing_file_depth": 0, "drv": "null-co", diff --git a/tests/qemu-iotests/267.out b/tests/qemu-iotests/267.out index f6f5d8715a..37b7ebd280 100644 --- a/tests/qemu-iotests/267.out +++ b/tests/qemu-iotests/267.out @@ -8,7 +8,7 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 Error: no block device can store vmstate for snapshot (qemu) info snapshots -no block device can store vmstate for snapshot +Error: no block device can store vmstate for snapshot (qemu) loadvm snap0 Error: no block device can store vmstate for snapshot (qemu) quit @@ -22,7 +22,7 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 Error: Device 'none0' is writable but does not support snapshots (qemu) info snapshots -no block device can store vmstate for snapshot +Error: no block device can store vmstate for snapshot (qemu) loadvm snap0 Error: Device 'none0' is writable but does not support snapshots (qemu) quit @@ -58,7 +58,7 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 Error: Device 'virtio0' is writable but does not support snapshots (qemu) info snapshots -no block device can store vmstate for snapshot +Error: no block device can store vmstate for snapshot (qemu) loadvm snap0 Error: Device 'virtio0' is writable but does not support snapshots (qemu) quit @@ -83,7 +83,7 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 Error: Device 'file' is writable but does not support snapshots (qemu) info snapshots -no block device can store vmstate for snapshot +Error: no block device can store vmstate for snapshot (qemu) loadvm snap0 Error: Device 'file' is writable but does not support snapshots (qemu) quit diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 511a55b1e8..26e6b45b04 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -229,6 +229,7 @@ _filter_img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' regex_json_child_start='^ *"children": \[' + regex_json_limit_start='^ *"limits": \{' gsed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ @@ -261,7 +262,7 @@ _filter_img_info() discard=1 elif [[ $line =~ "Child node '/" ]]; then discard=1 - elif [[ $line =~ $regex_json_spec_start ]]; then + elif [[ $line =~ $regex_json_spec_start || $line =~ $regex_json_limit_start ]]; then discard=2 regex_json_end="^${line%%[^ ]*}\\},? *$" elif [[ $line =~ $regex_json_child_start ]]; then diff --git a/tests/qemu-iotests/tests/fuse-allow-other.out b/tests/qemu-iotests/tests/fuse-allow-other.out index 543fa52a06..3219fc35e0 100644 --- a/tests/qemu-iotests/tests/fuse-allow-other.out +++ b/tests/qemu-iotests/tests/fuse-allow-other.out @@ -28,9 +28,9 @@ stat: cannot statx 'fuse-export': Permission denied cat: fuse-export: Permission denied stat: cannot statx 'fuse-export': Permission denied {'execute': 'quit'} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} +{"return": {}} --- allow-other=on --- {'execute': 'qmp_capabilities'} @@ -55,9 +55,9 @@ Permissions seen by nobody: 444 cat: fuse-export: Permission denied Permissions seen by nobody: 440 {'execute': 'quit'} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} +{"return": {}} --- allow-other=auto --- {'execute': 'qmp_capabilities'} @@ -82,7 +82,7 @@ Permissions seen by nobody: 444 cat: fuse-export: Permission denied Permissions seen by nobody: 440 {'execute': 'quit'} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/tests/inactive-node-nbd b/tests/qemu-iotests/tests/inactive-node-nbd index a95b37e796..664157bfd0 100755 --- a/tests/qemu-iotests/tests/inactive-node-nbd +++ b/tests/qemu-iotests/tests/inactive-node-nbd @@ -24,6 +24,7 @@ from iotests import QemuIoInteractive from iotests import filter_qemu_io, filter_qtest, filter_qmp_testfiles iotests.script_initialize(supported_fmts=['generic'], + unsupported_fmts=['luks'], supported_protocols=['file'], supported_platforms=['linux']) diff --git a/tests/qemu-iotests/tests/resize-below-filter b/tests/qemu-iotests/tests/resize-below-filter new file mode 100755 index 0000000000..f55619cf34 --- /dev/null +++ b/tests/qemu-iotests/tests/resize-below-filter @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Test what happens when a node below filter nodes is resized. +# +# Copyright (C) Proxmox Server Solutions GmbH +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import iotests +from iotests import imgfmt, qemu_img_create, QMPTestCase + +image_size = 1 * 1024 * 1024 +image = os.path.join(iotests.test_dir, 'test.img') + +class TestResizeBelowFilter(QMPTestCase): + def setUp(self) -> None: + qemu_img_create('-f', imgfmt, image, str(image_size)) + + self.vm = iotests.VM() + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': imgfmt, + 'node-name': 'node0', + 'file': { + 'driver': 'file', + 'filename': image, + } + })) + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': 'compress', + 'node-name': 'comp0', + 'file': 'node0', + })) + self.vm.add_object('throttle-group,id=thrgr0') + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': 'throttle', + 'node-name': 'thr0', + 'throttle-group': 'thrgr0', + 'file': 'comp0', + })) + self.vm.add_object('throttle-group,id=thrgr1') + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': 'throttle', + 'node-name': 'thr1', + 'throttle-group': 'thrgr1', + 'file': 'node0', + })) + self.vm.launch() + + def tearDown(self) -> None: + self.vm.shutdown() + os.remove(image) + + def assert_size(self, size: int) -> None: + nodes = self.vm.qmp('query-named-block-nodes', flat=True)['return'] + self.assertEqual(len(nodes), 5) + for node in nodes: + if node['drv'] == 'file': + continue + self.assertEqual(node['image']['virtual-size'], size) + + def test_resize_below_filter(self) -> None: + self.assert_size(image_size) + self.vm.qmp('block_resize', node_name='thr0', size=2*image_size) + self.assert_size(2*image_size) + self.vm.qmp('block_resize', node_name='comp0', size=3*image_size) + self.assert_size(3*image_size) + self.vm.qmp('block_resize', node_name='node0', size=4*image_size) + self.assert_size(4*image_size) + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/resize-below-filter.out b/tests/qemu-iotests/tests/resize-below-filter.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/resize-below-filter.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/tests/resize-below-raw b/tests/qemu-iotests/tests/resize-below-raw new file mode 100755 index 0000000000..3c9241c918 --- /dev/null +++ b/tests/qemu-iotests/tests/resize-below-raw @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Test what happens when a 'file' node below a 'raw' node is resized. +# +# Copyright (C) Proxmox Server Solutions GmbH +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import iotests +from iotests import imgfmt, qemu_img_create, QMPTestCase + +image_size = 1 * 1024 * 1024 +image = os.path.join(iotests.test_dir, 'test.img') + +class TestResizeBelowRaw(QMPTestCase): + def setUp(self) -> None: + qemu_img_create('-f', imgfmt, image, str(image_size)) + + self.vm = iotests.VM() + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': imgfmt, + 'node-name': 'node0', + 'file': { + 'driver': 'file', + 'filename': image, + 'node-name': 'file0', + } + })) + self.vm.launch() + + def tearDown(self) -> None: + self.vm.shutdown() + os.remove(image) + + def assert_size(self, size: int) -> None: + nodes = self.vm.qmp('query-named-block-nodes', flat=True)['return'] + self.assertEqual(len(nodes), 2) + for node in nodes: + if node['drv'] == 'file': + continue + self.assertEqual(node['image']['virtual-size'], size) + + def test_resize_below_raw(self) -> None: + self.assert_size(image_size) + self.vm.qmp('block_resize', node_name='file0', size=2*image_size) + self.assert_size(2*image_size) + self.vm.qmp('block_resize', node_name='node0', size=3*image_size) + self.assert_size(3*image_size) + +if __name__ == '__main__': + iotests.main(supported_fmts=['raw'], supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/resize-below-raw.out b/tests/qemu-iotests/tests/resize-below-raw.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/resize-below-raw.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK