qemu-img info: Optionally show block limits
Add a new --limits option to 'qemu-img info' that displays the block limits for the image and all of its children, making the information more accessible for human users than in QMP. This option is not enabled by default because it can be a lot of output that isn't usually relevant if you're not specifically trying to diagnose some I/O problem. This makes the same information automatically also available in HMP 'info block -v'. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Hanna Czenczek <hreitz@redhat.com> Message-ID: <20251024123041.51254-4-kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
d2634e1828
commit
5b4b3bfdfc
5 changed files with 52 additions and 9 deletions
34
block/qapi.c
34
block/qapi.c
|
|
@ -417,6 +417,7 @@ fail:
|
||||||
*/
|
*/
|
||||||
void bdrv_query_block_graph_info(BlockDriverState *bs,
|
void bdrv_query_block_graph_info(BlockDriverState *bs,
|
||||||
BlockGraphInfo **p_info,
|
BlockGraphInfo **p_info,
|
||||||
|
bool limits,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ERRP_GUARD();
|
ERRP_GUARD();
|
||||||
|
|
@ -425,7 +426,7 @@ void bdrv_query_block_graph_info(BlockDriverState *bs,
|
||||||
BdrvChild *c;
|
BdrvChild *c;
|
||||||
|
|
||||||
info = g_new0(BlockGraphInfo, 1);
|
info = g_new0(BlockGraphInfo, 1);
|
||||||
bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), false, errp);
|
bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), limits, errp);
|
||||||
if (*errp) {
|
if (*errp) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -439,7 +440,7 @@ void bdrv_query_block_graph_info(BlockDriverState *bs,
|
||||||
QAPI_LIST_APPEND(children_list_tail, c_info);
|
QAPI_LIST_APPEND(children_list_tail, c_info);
|
||||||
|
|
||||||
c_info->name = g_strdup(c->name);
|
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) {
|
if (*errp) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -936,6 +937,29 @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
|
||||||
visit_free(v);
|
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
|
* Print the given @info object in human-readable form. Every field is indented
|
||||||
* using the given @indentation (four spaces per indentation level).
|
* using the given @indentation (four spaces per indentation level).
|
||||||
|
|
@ -1011,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) {
|
if (info->has_snapshots) {
|
||||||
SnapshotInfoList *elem;
|
SnapshotInfoList *elem;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -503,7 +503,7 @@ Command description:
|
||||||
|
|
||||||
The size syntax is similar to :manpage:`dd(1)`'s size syntax.
|
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] [-U] FILENAME
|
||||||
|
|
||||||
Give information about the disk image *FILENAME*. Use it in
|
Give information about the disk image *FILENAME*. Use it in
|
||||||
particular to know the size reserved on disk which can be different
|
particular to know the size reserved on disk which can be different
|
||||||
|
|
@ -571,6 +571,10 @@ Command description:
|
||||||
``ImageInfoSpecific*`` QAPI object (e.g. ``ImageInfoSpecificQCow2``
|
``ImageInfoSpecific*`` QAPI object (e.g. ``ImageInfoSpecificQCow2``
|
||||||
for qcow2 images).
|
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
|
.. 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.
|
Dump the metadata of image *FILENAME* and its backing file chain.
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ bdrv_query_image_info(BlockDriverState *bs, ImageInfo **p_info, bool flat,
|
||||||
bool skip_implicit_filters, Error **errp);
|
bool skip_implicit_filters, Error **errp);
|
||||||
void GRAPH_RDLOCK
|
void GRAPH_RDLOCK
|
||||||
bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info,
|
bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info,
|
||||||
Error **errp);
|
bool limits, Error **errp);
|
||||||
|
|
||||||
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
|
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
|
||||||
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
|
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,9 @@ SRST
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
DEF("info", img_info,
|
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] [-U] filename")
|
||||||
SRST
|
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] [-U] FILENAME
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
DEF("map", img_map,
|
DEF("map", img_map,
|
||||||
|
|
|
||||||
15
qemu-img.c
15
qemu-img.c
|
|
@ -86,6 +86,7 @@ enum {
|
||||||
OPTION_BITMAPS = 275,
|
OPTION_BITMAPS = 275,
|
||||||
OPTION_FORCE = 276,
|
OPTION_FORCE = 276,
|
||||||
OPTION_SKIP_BROKEN = 277,
|
OPTION_SKIP_BROKEN = 277,
|
||||||
|
OPTION_LIMITS = 278,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum OutputFormat {
|
typedef enum OutputFormat {
|
||||||
|
|
@ -3002,7 +3003,8 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
|
||||||
static BlockGraphInfoList *collect_image_info_list(bool image_opts,
|
static BlockGraphInfoList *collect_image_info_list(bool image_opts,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
const char *fmt,
|
const char *fmt,
|
||||||
bool chain, bool force_share)
|
bool chain, bool limits,
|
||||||
|
bool force_share)
|
||||||
{
|
{
|
||||||
BlockGraphInfoList *head = NULL;
|
BlockGraphInfoList *head = NULL;
|
||||||
BlockGraphInfoList **tail = &head;
|
BlockGraphInfoList **tail = &head;
|
||||||
|
|
@ -3039,7 +3041,7 @@ static BlockGraphInfoList *collect_image_info_list(bool image_opts,
|
||||||
* the chain manually here.
|
* the chain manually here.
|
||||||
*/
|
*/
|
||||||
bdrv_graph_rdlock_main_loop();
|
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();
|
bdrv_graph_rdunlock_main_loop();
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -3088,6 +3090,7 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv)
|
||||||
BlockGraphInfoList *list;
|
BlockGraphInfoList *list;
|
||||||
bool image_opts = false;
|
bool image_opts = false;
|
||||||
bool force_share = false;
|
bool force_share = false;
|
||||||
|
bool limits = false;
|
||||||
|
|
||||||
fmt = NULL;
|
fmt = NULL;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
|
|
@ -3097,6 +3100,7 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv)
|
||||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||||
{"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN},
|
{"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN},
|
||||||
{"force-share", no_argument, 0, 'U'},
|
{"force-share", no_argument, 0, 'U'},
|
||||||
|
{"limits", no_argument, 0, OPTION_LIMITS},
|
||||||
{"output", required_argument, 0, OPTION_OUTPUT},
|
{"output", required_argument, 0, OPTION_OUTPUT},
|
||||||
{"object", required_argument, 0, OPTION_OBJECT},
|
{"object", required_argument, 0, OPTION_OBJECT},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
|
|
@ -3119,6 +3123,8 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv)
|
||||||
" display information about the backing chain for copy-on-write overlays\n"
|
" display information about the backing chain for copy-on-write overlays\n"
|
||||||
" -U, --force-share\n"
|
" -U, --force-share\n"
|
||||||
" open image in shared mode for concurrent access\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"
|
" --output human|json\n"
|
||||||
" specify output format (default: human)\n"
|
" specify output format (default: human)\n"
|
||||||
" --object OBJDEF\n"
|
" --object OBJDEF\n"
|
||||||
|
|
@ -3140,6 +3146,9 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv)
|
||||||
case 'U':
|
case 'U':
|
||||||
force_share = true;
|
force_share = true;
|
||||||
break;
|
break;
|
||||||
|
case OPTION_LIMITS:
|
||||||
|
limits = true;
|
||||||
|
break;
|
||||||
case OPTION_OUTPUT:
|
case OPTION_OUTPUT:
|
||||||
output_format = parse_output_format(argv[0], optarg);
|
output_format = parse_output_format(argv[0], optarg);
|
||||||
break;
|
break;
|
||||||
|
|
@ -3156,7 +3165,7 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv)
|
||||||
filename = argv[optind++];
|
filename = argv[optind++];
|
||||||
|
|
||||||
list = collect_image_info_list(image_opts, filename, fmt, chain,
|
list = collect_image_info_list(image_opts, filename, fmt, chain,
|
||||||
force_share);
|
limits, force_share);
|
||||||
if (!list) {
|
if (!list) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue