From patchwork Mon Jan 28 17:39:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Kuchin X-Patchwork-Id: 10784159 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 82454746 for ; Mon, 28 Jan 2019 17:40:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 67CDF2BCBE for ; Mon, 28 Jan 2019 17:40:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5BF342BCEB; Mon, 28 Jan 2019 17:40:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 242B12BCBE for ; Mon, 28 Jan 2019 17:40:51 +0000 (UTC) Received: from localhost ([127.0.0.1]:35951 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1goAtu-00033P-GF for patchwork-qemu-devel@patchwork.kernel.org; Mon, 28 Jan 2019 12:40:50 -0500 Received: from eggs.gnu.org ([209.51.188.92]:59989) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1goAsX-0001py-HY for qemu-devel@nongnu.org; Mon, 28 Jan 2019 12:39:29 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1goAsQ-00033z-Gl for qemu-devel@nongnu.org; Mon, 28 Jan 2019 12:39:23 -0500 Received: from forwardcorp1o.cmail.yandex.net ([2a02:6b8:0:1a72::290]:48049) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1goAsI-0002ru-BZ; Mon, 28 Jan 2019 12:39:10 -0500 Received: from mxbackcorp2j.mail.yandex.net (mxbackcorp2j.mail.yandex.net [IPv6:2a02:6b8:0:1619::119]) by forwardcorp1o.cmail.yandex.net (Yandex) with ESMTP id EF44C217FF; Mon, 28 Jan 2019 20:39:07 +0300 (MSK) Received: from smtpcorp1j.mail.yandex.net (smtpcorp1j.mail.yandex.net [2a02:6b8:0:1619::137]) by mxbackcorp2j.mail.yandex.net (nwsmtp/Yandex) with ESMTP id fKGOjPM8Ke-d7W8NDLn; Mon, 28 Jan 2019 20:39:07 +0300 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1548697147; bh=PXQwWSvcqPap49Rz1iSy81np2d8a+Sdm+TLrYsIjJi4=; h=From:To:Cc:Subject:Date:Message-Id; b=hSRD98rz28ZCwAetv2pf0YEiPkjxHkFVtwEIxkeJE5TAmzzTVc9NfF3mfy6hL4/mB +mUHTUC24nk+33kpJD6ZqfdXXDCQQooPltdEinQii4wRc5XAUV1DUz/Z5E4r3ozHI9 Z9S0EmJ1YvT4cMOZ+900fUI6HE1MlUVA1Zy/7hys= Authentication-Results: mxbackcorp2j.mail.yandex.net; dkim=pass header.i=@yandex-team.ru Received: from dynamic-red.dhcp.yndx.net (dynamic-red.dhcp.yndx.net [2a02:6b8:0:408:250:b6ff:fe97:2682]) by smtpcorp1j.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id rRVBnUSaDG-d74CvQbv; Mon, 28 Jan 2019 20:39:07 +0300 (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client certificate not present) From: Anton Kuchin To: qemu-devel@nongnu.org Date: Mon, 28 Jan 2019 20:39:01 +0300 Message-Id: <20190128173901.3357-1-antonkuchin@yandex-team.ru> X-Mailer: git-send-email 2.19.1 MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a02:6b8:0:1a72::290 Subject: [Qemu-devel] [PATCH] block: split block/qapi.c to avoid linking utilities with qapi X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , qemu-block@nongnu.org, "Dr. David Alan Gilbert" , Markus Armbruster , Anton Kuchin , Max Reitz , Evgeny Yakovlev Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Only part of block/qapi.c is used by qemu-io qemu-nbd and qemu-img and its not realy about QAPI, so move it to separate file to reduce amount of unused code linked to utilities and avoid unnecessary dependencies. Signed-off-by: Anton Kuchin --- block.c | 2 +- block/Makefile.objs | 3 +- block/bdrv_info.c | 582 ++++++++++++++++++++++++++ block/qapi.c | 553 +----------------------- hmp.c | 2 +- include/block/{qapi.h => bdrv_info.h} | 6 +- monitor.c | 2 +- qemu-img.c | 2 +- qemu-io-cmds.c | 2 +- 9 files changed, 593 insertions(+), 561 deletions(-) create mode 100644 block/bdrv_info.c rename include/block/{qapi.h => bdrv_info.h} (97%) diff --git a/block.c b/block.c index 4f5ff2cc12..a14d64c2fd 100644 --- a/block.c +++ b/block.c @@ -43,7 +43,7 @@ #include "qemu/notify.h" #include "qemu/option.h" #include "qemu/coroutine.h" -#include "block/qapi.h" +#include "block/bdrv_info.h" #include "qemu/timer.h" #include "qemu/cutils.h" #include "qemu/id.h" diff --git a/block/Makefile.objs b/block/Makefile.objs index 7a81892a52..aab92d59ec 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -14,7 +14,7 @@ block-obj-y += quorum.o block-obj-y += blkdebug.o blkverify.o blkreplay.o block-obj-$(CONFIG_PARALLELS) += parallels.o block-obj-y += blklogwrites.o -block-obj-y += block-backend.o snapshot.o qapi.o +block-obj-y += block-backend.o snapshot.o bdrv_info.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += file-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o @@ -41,6 +41,7 @@ block-obj-y += throttle.o copy-on-read.o block-obj-y += crypto.o common-obj-y += stream.o +common-obj-y += qapi.o nfs.o-libs := $(LIBNFS_LIBS) iscsi.o-cflags := $(LIBISCSI_CFLAGS) diff --git a/block/bdrv_info.c b/block/bdrv_info.c new file mode 100644 index 0000000000..425045421b --- /dev/null +++ b/block/bdrv_info.c @@ -0,0 +1,582 @@ +/* + * Block layer qmp and info dump related functions + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/bdrv_info.h" +#include "block/block_int.h" +#include "block/write-threshold.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-block-core.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/qapi-visit-block-core.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" + +BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, + BlockDriverState *bs, Error **errp) +{ + ImageInfo **p_image_info; + BlockDriverState *bs0; + BlockDeviceInfo *info; + + if (!bs->drv) { + error_setg(errp, "Block device %s is ejected", bs->node_name); + return NULL; + } + + info = g_malloc0(sizeof(*info)); + info->file = g_strdup(bs->filename); + info->ro = bs->read_only; + info->drv = g_strdup(bs->drv->format_name); + info->encrypted = bs->encrypted; + info->encryption_key_missing = false; + + info->cache = g_new(BlockdevCacheInfo, 1); + *info->cache = (BlockdevCacheInfo) { + .writeback = blk ? blk_enable_write_cache(blk) : true, + .direct = !!(bs->open_flags & BDRV_O_NOCACHE), + .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), + }; + + if (bs->node_name[0]) { + info->has_node_name = true; + info->node_name = g_strdup(bs->node_name); + } + + if (bs->backing_file[0]) { + info->has_backing_file = true; + info->backing_file = g_strdup(bs->backing_file); + } + + info->detect_zeroes = bs->detect_zeroes; + + if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { + ThrottleConfig cfg; + BlockBackendPublic *blkp = blk_get_public(blk); + + throttle_group_get_config(&blkp->throttle_group_member, &cfg); + + info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; + info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; + info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; + + info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg; + info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg; + info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg; + + info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; + info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; + info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; + info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; + info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; + info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; + + info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; + info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; + info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; + info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; + info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; + info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; + + info->has_bps_max_length = info->has_bps_max; + info->bps_max_length = + cfg.buckets[THROTTLE_BPS_TOTAL].burst_length; + info->has_bps_rd_max_length = info->has_bps_rd_max; + info->bps_rd_max_length = + cfg.buckets[THROTTLE_BPS_READ].burst_length; + info->has_bps_wr_max_length = info->has_bps_wr_max; + info->bps_wr_max_length = + cfg.buckets[THROTTLE_BPS_WRITE].burst_length; + + info->has_iops_max_length = info->has_iops_max; + info->iops_max_length = + cfg.buckets[THROTTLE_OPS_TOTAL].burst_length; + info->has_iops_rd_max_length = info->has_iops_rd_max; + info->iops_rd_max_length = + cfg.buckets[THROTTLE_OPS_READ].burst_length; + info->has_iops_wr_max_length = info->has_iops_wr_max; + info->iops_wr_max_length = + cfg.buckets[THROTTLE_OPS_WRITE].burst_length; + + info->has_iops_size = cfg.op_size; + info->iops_size = cfg.op_size; + + info->has_group = true; + info->group = + g_strdup(throttle_group_get_name(&blkp->throttle_group_member)); + } + + info->write_threshold = bdrv_write_threshold_get(bs); + + bs0 = bs; + p_image_info = &info->image; + info->backing_file_depth = 0; + while (1) { + Error *local_err = NULL; + bdrv_query_image_info(bs0, p_image_info, &local_err); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_BlockDeviceInfo(info); + return NULL; + } + + if (bs0->drv && bs0->backing) { + info->backing_file_depth++; + bs0 = bs0->backing->bs; + (*p_image_info)->has_backing_image = true; + p_image_info = &((*p_image_info)->backing_image); + } else { + break; + } + + /* Skip automatically inserted nodes that the user isn't aware of for + * query-block (blk != NULL), but not for query-named-block-nodes */ + while (blk && bs0->drv && bs0->implicit) { + bs0 = backing_bs(bs0); + assert(bs0); + } + } + + return info; +} + +/* + * Returns 0 on success, with *p_list either set to describe snapshot + * information, or NULL because there are no snapshots. Returns -errno on + * error, with *p_list untouched. + */ +int bdrv_query_snapshot_info_list(BlockDriverState *bs, + SnapshotInfoList **p_list, + Error **errp) +{ + int i, sn_count; + QEMUSnapshotInfo *sn_tab = NULL; + SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL; + SnapshotInfo *info; + + sn_count = bdrv_snapshot_list(bs, &sn_tab); + if (sn_count < 0) { + const char *dev = bdrv_get_device_name(bs); + switch (sn_count) { + case -ENOMEDIUM: + error_setg(errp, "Device '%s' is not inserted", dev); + break; + case -ENOTSUP: + error_setg(errp, + "Device '%s' does not support internal snapshots", + dev); + break; + default: + error_setg_errno(errp, -sn_count, + "Can't list snapshots of device '%s'", dev); + break; + } + return sn_count; + } + + for (i = 0; i < sn_count; i++) { + info = g_new0(SnapshotInfo, 1); + info->id = g_strdup(sn_tab[i].id_str); + info->name = g_strdup(sn_tab[i].name); + info->vm_state_size = sn_tab[i].vm_state_size; + info->date_sec = sn_tab[i].date_sec; + info->date_nsec = sn_tab[i].date_nsec; + info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; + info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; + + info_list = g_new0(SnapshotInfoList, 1); + info_list->value = info; + + /* XXX: waiting for the qapi to support qemu-queue.h types */ + if (!cur_item) { + head = cur_item = info_list; + } else { + cur_item->next = info_list; + cur_item = info_list; + } + + } + + g_free(sn_tab); + *p_list = head; + return 0; +} + +/** + * bdrv_query_image_info: + * @bs: block device to examine + * @p_info: location to store image information + * @errp: location to store error information + * + * Store "flat" image information in @p_info. + * + * "Flat" means it does *not* query backing image information, + * i.e. (*pinfo)->has_backing_image will be set to false and + * (*pinfo)->backing_image to NULL even when the image does in fact have + * a backing image. + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_image_info(BlockDriverState *bs, + ImageInfo **p_info, + Error **errp) +{ + int64_t size; + const char *backing_filename; + BlockDriverInfo bdi; + int ret; + Error *err = NULL; + ImageInfo *info; + + aio_context_acquire(bdrv_get_aio_context(bs)); + + size = bdrv_getlength(bs); + if (size < 0) { + error_setg_errno(errp, -size, "Can't get image size '%s'", + bs->exact_filename); + goto out; + } + + info = g_new0(ImageInfo, 1); + info->filename = g_strdup(bs->filename); + info->format = g_strdup(bdrv_get_format_name(bs)); + info->virtual_size = size; + info->actual_size = bdrv_get_allocated_file_size(bs); + info->has_actual_size = info->actual_size >= 0; + if (bdrv_is_encrypted(bs)) { + info->encrypted = true; + info->has_encrypted = true; + } + if (bdrv_get_info(bs, &bdi) >= 0) { + if (bdi.cluster_size != 0) { + info->cluster_size = bdi.cluster_size; + info->has_cluster_size = true; + } + info->dirty_flag = bdi.is_dirty; + info->has_dirty_flag = true; + } + info->format_specific = bdrv_get_specific_info(bs); + info->has_format_specific = info->format_specific != NULL; + + backing_filename = bs->backing_file; + if (backing_filename[0] != '\0') { + char *backing_filename2 = g_malloc0(PATH_MAX); + info->backing_filename = g_strdup(backing_filename); + info->has_backing_filename = true; + bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err); + if (err) { + /* Can't reconstruct the full backing filename, so we must omit + * this field and apply a Best Effort to this query. */ + g_free(backing_filename2); + backing_filename2 = NULL; + error_free(err); + err = NULL; + } + + /* Always report the full_backing_filename if present, even if it's the + * same as backing_filename. That they are same is useful info. */ + if (backing_filename2) { + info->full_backing_filename = g_strdup(backing_filename2); + info->has_full_backing_filename = true; + } + + if (bs->backing_format[0]) { + info->backing_filename_format = g_strdup(bs->backing_format); + info->has_backing_filename_format = true; + } + g_free(backing_filename2); + } + + ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); + switch (ret) { + case 0: + if (info->snapshots) { + info->has_snapshots = true; + } + break; + /* recoverable error */ + case -ENOMEDIUM: + case -ENOTSUP: + error_free(err); + break; + default: + error_propagate(errp, err); + qapi_free_ImageInfo(info); + goto out; + } + + *p_info = info; + +out: + aio_context_release(bdrv_get_aio_context(bs)); +} + +#define NB_SUFFIXES 4 + +static char *get_human_readable_size(char *buf, int buf_size, int64_t size) +{ + static const char suffixes[NB_SUFFIXES] = {'K', 'M', 'G', 'T'}; + int64_t base; + int i; + + if (size <= 999) { + snprintf(buf, buf_size, "%" PRId64, size); + } else { + base = 1024; + for (i = 0; i < NB_SUFFIXES; i++) { + if (size < (10 * base)) { + snprintf(buf, buf_size, "%0.1f%c", + (double)size / base, + suffixes[i]); + break; + } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { + snprintf(buf, buf_size, "%" PRId64 "%c", + ((size + (base >> 1)) / base), + suffixes[i]); + break; + } + base = base * 1024; + } + } + return buf; +} + +void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, + QEMUSnapshotInfo *sn) +{ + char buf1[128], date_buf[128], clock_buf[128]; + struct tm tm; + time_t ti; + int64_t secs; + + if (!sn) { + func_fprintf(f, + "%-10s%-20s%7s%20s%15s", + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); + } else { + ti = sn->date_sec; + localtime_r(&ti, &tm); + strftime(date_buf, sizeof(date_buf), + "%Y-%m-%d %H:%M:%S", &tm); + secs = sn->vm_clock_nsec / 1000000000; + snprintf(clock_buf, sizeof(clock_buf), + "%02d:%02d:%02d.%03d", + (int)(secs / 3600), + (int)((secs / 60) % 60), + (int)(secs % 60), + (int)((sn->vm_clock_nsec / 1000000) % 1000)); + func_fprintf(f, + "%-10s%-20s%7s%20s%15s", + sn->id_str, sn->name, + get_human_readable_size(buf1, sizeof(buf1), + sn->vm_state_size), + date_buf, + clock_buf); + } +} + +static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, + QDict *dict); +static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, + QList *list); + +static void dump_qobject(fprintf_function func_fprintf, void *f, + int comp_indent, QObject *obj) +{ + switch (qobject_type(obj)) { + case QTYPE_QNUM: { + QNum *value = qobject_to(QNum, obj); + char *tmp = qnum_to_string(value); + func_fprintf(f, "%s", tmp); + g_free(tmp); + break; + } + case QTYPE_QSTRING: { + QString *value = qobject_to(QString, obj); + func_fprintf(f, "%s", qstring_get_str(value)); + break; + } + case QTYPE_QDICT: { + QDict *value = qobject_to(QDict, obj); + dump_qdict(func_fprintf, f, comp_indent, value); + break; + } + case QTYPE_QLIST: { + QList *value = qobject_to(QList, obj); + dump_qlist(func_fprintf, f, comp_indent, value); + break; + } + case QTYPE_QBOOL: { + QBool *value = qobject_to(QBool, obj); + func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false"); + break; + } + default: + abort(); + } +} + +static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, + QList *list) +{ + const QListEntry *entry; + int i = 0; + + for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) { + QType type = qobject_type(entry->value); + bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); + func_fprintf(f, "%*s[%i]:%c", indentation * 4, "", i, + composite ? '\n' : ' '); + dump_qobject(func_fprintf, f, indentation + 1, entry->value); + if (!composite) { + func_fprintf(f, "\n"); + } + } +} + +static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, + QDict *dict) +{ + const QDictEntry *entry; + + for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) { + QType type = qobject_type(entry->value); + bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); + char *key = g_malloc(strlen(entry->key) + 1); + int i; + + /* replace dashes with spaces in key (variable) names */ + for (i = 0; entry->key[i]; i++) { + key[i] = entry->key[i] == '-' ? ' ' : entry->key[i]; + } + key[i] = 0; + func_fprintf(f, "%*s%s:%c", indentation * 4, "", key, + composite ? '\n' : ' '); + dump_qobject(func_fprintf, f, indentation + 1, entry->value); + if (!composite) { + func_fprintf(f, "\n"); + } + g_free(key); + } +} + +void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, + ImageInfoSpecific *info_spec) +{ + QObject *obj, *data; + Visitor *v = qobject_output_visitor_new(&obj); + + visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); + visit_complete(v, &obj); + data = qdict_get(qobject_to(QDict, obj), "data"); + dump_qobject(func_fprintf, f, 1, data); + qobject_unref(obj); + visit_free(v); +} + +void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, + ImageInfo *info) +{ + char size_buf[128], dsize_buf[128]; + if (!info->has_actual_size) { + snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); + } else { + get_human_readable_size(dsize_buf, sizeof(dsize_buf), + info->actual_size); + } + get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size); + func_fprintf(f, + "image: %s\n" + "file format: %s\n" + "virtual size: %s (%" PRId64 " bytes)\n" + "disk size: %s\n", + info->filename, info->format, size_buf, + info->virtual_size, + dsize_buf); + + if (info->has_encrypted && info->encrypted) { + func_fprintf(f, "encrypted: yes\n"); + } + + if (info->has_cluster_size) { + func_fprintf(f, "cluster_size: %" PRId64 "\n", + info->cluster_size); + } + + if (info->has_dirty_flag && info->dirty_flag) { + func_fprintf(f, "cleanly shut down: no\n"); + } + + if (info->has_backing_filename) { + func_fprintf(f, "backing file: %s", info->backing_filename); + if (!info->has_full_backing_filename) { + func_fprintf(f, " (cannot determine actual path)"); + } else if (strcmp(info->backing_filename, + info->full_backing_filename) != 0) { + func_fprintf(f, " (actual path: %s)", info->full_backing_filename); + } + func_fprintf(f, "\n"); + if (info->has_backing_filename_format) { + func_fprintf(f, "backing file format: %s\n", + info->backing_filename_format); + } + } + + if (info->has_snapshots) { + SnapshotInfoList *elem; + + func_fprintf(f, "Snapshot list:\n"); + bdrv_snapshot_dump(func_fprintf, f, NULL); + func_fprintf(f, "\n"); + + /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but + * we convert to the block layer's native QEMUSnapshotInfo for now. + */ + for (elem = info->snapshots; elem; elem = elem->next) { + QEMUSnapshotInfo sn = { + .vm_state_size = elem->value->vm_state_size, + .date_sec = elem->value->date_sec, + .date_nsec = elem->value->date_nsec, + .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + + elem->value->vm_clock_nsec, + }; + + pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); + pstrcpy(sn.name, sizeof(sn.name), elem->value->name); + bdrv_snapshot_dump(func_fprintf, f, &sn); + func_fprintf(f, "\n"); + } + } + + if (info->has_format_specific) { + func_fprintf(f, "Format specific information:\n"); + bdrv_image_info_specific_dump(func_fprintf, f, info->format_specific); + } +} + diff --git a/block/qapi.c b/block/qapi.c index c66f949db8..1ed7ea624f 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -23,320 +23,11 @@ */ #include "qemu/osdep.h" -#include "block/qapi.h" -#include "block/block_int.h" +#include "block/bdrv_info.h" #include "block/throttle-groups.h" -#include "block/write-threshold.h" #include "qapi/error.h" #include "qapi/qapi-commands-block-core.h" -#include "qapi/qobject-output-visitor.h" -#include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" #include "sysemu/block-backend.h" -#include "qemu/cutils.h" - -BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, - BlockDriverState *bs, Error **errp) -{ - ImageInfo **p_image_info; - BlockDriverState *bs0; - BlockDeviceInfo *info; - - if (!bs->drv) { - error_setg(errp, "Block device %s is ejected", bs->node_name); - return NULL; - } - - info = g_malloc0(sizeof(*info)); - info->file = g_strdup(bs->filename); - info->ro = bs->read_only; - info->drv = g_strdup(bs->drv->format_name); - info->encrypted = bs->encrypted; - info->encryption_key_missing = false; - - info->cache = g_new(BlockdevCacheInfo, 1); - *info->cache = (BlockdevCacheInfo) { - .writeback = blk ? blk_enable_write_cache(blk) : true, - .direct = !!(bs->open_flags & BDRV_O_NOCACHE), - .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), - }; - - if (bs->node_name[0]) { - info->has_node_name = true; - info->node_name = g_strdup(bs->node_name); - } - - if (bs->backing_file[0]) { - info->has_backing_file = true; - info->backing_file = g_strdup(bs->backing_file); - } - - info->detect_zeroes = bs->detect_zeroes; - - if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { - ThrottleConfig cfg; - BlockBackendPublic *blkp = blk_get_public(blk); - - throttle_group_get_config(&blkp->throttle_group_member, &cfg); - - info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; - info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; - info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; - - info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg; - info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg; - info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg; - - info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; - info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; - info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; - info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; - info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; - info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; - - info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; - info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; - info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; - info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; - info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; - info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; - - info->has_bps_max_length = info->has_bps_max; - info->bps_max_length = - cfg.buckets[THROTTLE_BPS_TOTAL].burst_length; - info->has_bps_rd_max_length = info->has_bps_rd_max; - info->bps_rd_max_length = - cfg.buckets[THROTTLE_BPS_READ].burst_length; - info->has_bps_wr_max_length = info->has_bps_wr_max; - info->bps_wr_max_length = - cfg.buckets[THROTTLE_BPS_WRITE].burst_length; - - info->has_iops_max_length = info->has_iops_max; - info->iops_max_length = - cfg.buckets[THROTTLE_OPS_TOTAL].burst_length; - info->has_iops_rd_max_length = info->has_iops_rd_max; - info->iops_rd_max_length = - cfg.buckets[THROTTLE_OPS_READ].burst_length; - info->has_iops_wr_max_length = info->has_iops_wr_max; - info->iops_wr_max_length = - cfg.buckets[THROTTLE_OPS_WRITE].burst_length; - - info->has_iops_size = cfg.op_size; - info->iops_size = cfg.op_size; - - info->has_group = true; - info->group = - g_strdup(throttle_group_get_name(&blkp->throttle_group_member)); - } - - info->write_threshold = bdrv_write_threshold_get(bs); - - bs0 = bs; - p_image_info = &info->image; - info->backing_file_depth = 0; - while (1) { - Error *local_err = NULL; - bdrv_query_image_info(bs0, p_image_info, &local_err); - if (local_err) { - error_propagate(errp, local_err); - qapi_free_BlockDeviceInfo(info); - return NULL; - } - - if (bs0->drv && bs0->backing) { - info->backing_file_depth++; - bs0 = bs0->backing->bs; - (*p_image_info)->has_backing_image = true; - p_image_info = &((*p_image_info)->backing_image); - } else { - break; - } - - /* Skip automatically inserted nodes that the user isn't aware of for - * query-block (blk != NULL), but not for query-named-block-nodes */ - while (blk && bs0->drv && bs0->implicit) { - bs0 = backing_bs(bs0); - assert(bs0); - } - } - - return info; -} - -/* - * Returns 0 on success, with *p_list either set to describe snapshot - * information, or NULL because there are no snapshots. Returns -errno on - * error, with *p_list untouched. - */ -int bdrv_query_snapshot_info_list(BlockDriverState *bs, - SnapshotInfoList **p_list, - Error **errp) -{ - int i, sn_count; - QEMUSnapshotInfo *sn_tab = NULL; - SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL; - SnapshotInfo *info; - - sn_count = bdrv_snapshot_list(bs, &sn_tab); - if (sn_count < 0) { - const char *dev = bdrv_get_device_name(bs); - switch (sn_count) { - case -ENOMEDIUM: - error_setg(errp, "Device '%s' is not inserted", dev); - break; - case -ENOTSUP: - error_setg(errp, - "Device '%s' does not support internal snapshots", - dev); - break; - default: - error_setg_errno(errp, -sn_count, - "Can't list snapshots of device '%s'", dev); - break; - } - return sn_count; - } - - for (i = 0; i < sn_count; i++) { - info = g_new0(SnapshotInfo, 1); - info->id = g_strdup(sn_tab[i].id_str); - info->name = g_strdup(sn_tab[i].name); - info->vm_state_size = sn_tab[i].vm_state_size; - info->date_sec = sn_tab[i].date_sec; - info->date_nsec = sn_tab[i].date_nsec; - info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; - info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; - - info_list = g_new0(SnapshotInfoList, 1); - info_list->value = info; - - /* XXX: waiting for the qapi to support qemu-queue.h types */ - if (!cur_item) { - head = cur_item = info_list; - } else { - cur_item->next = info_list; - cur_item = info_list; - } - - } - - g_free(sn_tab); - *p_list = head; - return 0; -} - -/** - * bdrv_query_image_info: - * @bs: block device to examine - * @p_info: location to store image information - * @errp: location to store error information - * - * Store "flat" image information in @p_info. - * - * "Flat" means it does *not* query backing image information, - * i.e. (*pinfo)->has_backing_image will be set to false and - * (*pinfo)->backing_image to NULL even when the image does in fact have - * a backing image. - * - * @p_info will be set only on success. On error, store error in @errp. - */ -void bdrv_query_image_info(BlockDriverState *bs, - ImageInfo **p_info, - Error **errp) -{ - int64_t size; - const char *backing_filename; - BlockDriverInfo bdi; - int ret; - Error *err = NULL; - ImageInfo *info; - - aio_context_acquire(bdrv_get_aio_context(bs)); - - size = bdrv_getlength(bs); - if (size < 0) { - error_setg_errno(errp, -size, "Can't get image size '%s'", - bs->exact_filename); - goto out; - } - - info = g_new0(ImageInfo, 1); - info->filename = g_strdup(bs->filename); - info->format = g_strdup(bdrv_get_format_name(bs)); - info->virtual_size = size; - info->actual_size = bdrv_get_allocated_file_size(bs); - info->has_actual_size = info->actual_size >= 0; - if (bdrv_is_encrypted(bs)) { - info->encrypted = true; - info->has_encrypted = true; - } - if (bdrv_get_info(bs, &bdi) >= 0) { - if (bdi.cluster_size != 0) { - info->cluster_size = bdi.cluster_size; - info->has_cluster_size = true; - } - info->dirty_flag = bdi.is_dirty; - info->has_dirty_flag = true; - } - info->format_specific = bdrv_get_specific_info(bs); - info->has_format_specific = info->format_specific != NULL; - - backing_filename = bs->backing_file; - if (backing_filename[0] != '\0') { - char *backing_filename2 = g_malloc0(PATH_MAX); - info->backing_filename = g_strdup(backing_filename); - info->has_backing_filename = true; - bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err); - if (err) { - /* Can't reconstruct the full backing filename, so we must omit - * this field and apply a Best Effort to this query. */ - g_free(backing_filename2); - backing_filename2 = NULL; - error_free(err); - err = NULL; - } - - /* Always report the full_backing_filename if present, even if it's the - * same as backing_filename. That they are same is useful info. */ - if (backing_filename2) { - info->full_backing_filename = g_strdup(backing_filename2); - info->has_full_backing_filename = true; - } - - if (bs->backing_format[0]) { - info->backing_filename_format = g_strdup(bs->backing_format); - info->has_backing_filename_format = true; - } - g_free(backing_filename2); - } - - ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); - switch (ret) { - case 0: - if (info->snapshots) { - info->has_snapshots = true; - } - break; - /* recoverable error */ - case -ENOMEDIUM: - case -ENOTSUP: - error_free(err); - break; - default: - error_propagate(errp, err); - qapi_free_ImageInfo(info); - goto out; - } - - *p_info = info; - -out: - aio_context_release(bdrv_get_aio_context(bs)); -} /* @p_info will be set only on success. */ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, @@ -629,245 +320,3 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, return head; } -#define NB_SUFFIXES 4 - -static char *get_human_readable_size(char *buf, int buf_size, int64_t size) -{ - static const char suffixes[NB_SUFFIXES] = {'K', 'M', 'G', 'T'}; - int64_t base; - int i; - - if (size <= 999) { - snprintf(buf, buf_size, "%" PRId64, size); - } else { - base = 1024; - for (i = 0; i < NB_SUFFIXES; i++) { - if (size < (10 * base)) { - snprintf(buf, buf_size, "%0.1f%c", - (double)size / base, - suffixes[i]); - break; - } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { - snprintf(buf, buf_size, "%" PRId64 "%c", - ((size + (base >> 1)) / base), - suffixes[i]); - break; - } - base = base * 1024; - } - } - return buf; -} - -void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, - QEMUSnapshotInfo *sn) -{ - char buf1[128], date_buf[128], clock_buf[128]; - struct tm tm; - time_t ti; - int64_t secs; - - if (!sn) { - func_fprintf(f, - "%-10s%-20s%7s%20s%15s", - "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); - } else { - ti = sn->date_sec; - localtime_r(&ti, &tm); - strftime(date_buf, sizeof(date_buf), - "%Y-%m-%d %H:%M:%S", &tm); - secs = sn->vm_clock_nsec / 1000000000; - snprintf(clock_buf, sizeof(clock_buf), - "%02d:%02d:%02d.%03d", - (int)(secs / 3600), - (int)((secs / 60) % 60), - (int)(secs % 60), - (int)((sn->vm_clock_nsec / 1000000) % 1000)); - func_fprintf(f, - "%-10s%-20s%7s%20s%15s", - sn->id_str, sn->name, - get_human_readable_size(buf1, sizeof(buf1), - sn->vm_state_size), - date_buf, - clock_buf); - } -} - -static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, - QDict *dict); -static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, - QList *list); - -static void dump_qobject(fprintf_function func_fprintf, void *f, - int comp_indent, QObject *obj) -{ - switch (qobject_type(obj)) { - case QTYPE_QNUM: { - QNum *value = qobject_to(QNum, obj); - char *tmp = qnum_to_string(value); - func_fprintf(f, "%s", tmp); - g_free(tmp); - break; - } - case QTYPE_QSTRING: { - QString *value = qobject_to(QString, obj); - func_fprintf(f, "%s", qstring_get_str(value)); - break; - } - case QTYPE_QDICT: { - QDict *value = qobject_to(QDict, obj); - dump_qdict(func_fprintf, f, comp_indent, value); - break; - } - case QTYPE_QLIST: { - QList *value = qobject_to(QList, obj); - dump_qlist(func_fprintf, f, comp_indent, value); - break; - } - case QTYPE_QBOOL: { - QBool *value = qobject_to(QBool, obj); - func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false"); - break; - } - default: - abort(); - } -} - -static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, - QList *list) -{ - const QListEntry *entry; - int i = 0; - - for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) { - QType type = qobject_type(entry->value); - bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); - func_fprintf(f, "%*s[%i]:%c", indentation * 4, "", i, - composite ? '\n' : ' '); - dump_qobject(func_fprintf, f, indentation + 1, entry->value); - if (!composite) { - func_fprintf(f, "\n"); - } - } -} - -static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, - QDict *dict) -{ - const QDictEntry *entry; - - for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) { - QType type = qobject_type(entry->value); - bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); - char *key = g_malloc(strlen(entry->key) + 1); - int i; - - /* replace dashes with spaces in key (variable) names */ - for (i = 0; entry->key[i]; i++) { - key[i] = entry->key[i] == '-' ? ' ' : entry->key[i]; - } - key[i] = 0; - func_fprintf(f, "%*s%s:%c", indentation * 4, "", key, - composite ? '\n' : ' '); - dump_qobject(func_fprintf, f, indentation + 1, entry->value); - if (!composite) { - func_fprintf(f, "\n"); - } - g_free(key); - } -} - -void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, - ImageInfoSpecific *info_spec) -{ - QObject *obj, *data; - Visitor *v = qobject_output_visitor_new(&obj); - - visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); - visit_complete(v, &obj); - data = qdict_get(qobject_to(QDict, obj), "data"); - dump_qobject(func_fprintf, f, 1, data); - qobject_unref(obj); - visit_free(v); -} - -void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, - ImageInfo *info) -{ - char size_buf[128], dsize_buf[128]; - if (!info->has_actual_size) { - snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); - } else { - get_human_readable_size(dsize_buf, sizeof(dsize_buf), - info->actual_size); - } - get_human_readable_size(size_buf, sizeof(size_buf), info->virtual_size); - func_fprintf(f, - "image: %s\n" - "file format: %s\n" - "virtual size: %s (%" PRId64 " bytes)\n" - "disk size: %s\n", - info->filename, info->format, size_buf, - info->virtual_size, - dsize_buf); - - if (info->has_encrypted && info->encrypted) { - func_fprintf(f, "encrypted: yes\n"); - } - - if (info->has_cluster_size) { - func_fprintf(f, "cluster_size: %" PRId64 "\n", - info->cluster_size); - } - - if (info->has_dirty_flag && info->dirty_flag) { - func_fprintf(f, "cleanly shut down: no\n"); - } - - if (info->has_backing_filename) { - func_fprintf(f, "backing file: %s", info->backing_filename); - if (!info->has_full_backing_filename) { - func_fprintf(f, " (cannot determine actual path)"); - } else if (strcmp(info->backing_filename, - info->full_backing_filename) != 0) { - func_fprintf(f, " (actual path: %s)", info->full_backing_filename); - } - func_fprintf(f, "\n"); - if (info->has_backing_filename_format) { - func_fprintf(f, "backing file format: %s\n", - info->backing_filename_format); - } - } - - if (info->has_snapshots) { - SnapshotInfoList *elem; - - func_fprintf(f, "Snapshot list:\n"); - bdrv_snapshot_dump(func_fprintf, f, NULL); - func_fprintf(f, "\n"); - - /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but - * we convert to the block layer's native QEMUSnapshotInfo for now. - */ - for (elem = info->snapshots; elem; elem = elem->next) { - QEMUSnapshotInfo sn = { - .vm_state_size = elem->value->vm_state_size, - .date_sec = elem->value->date_sec, - .date_nsec = elem->value->date_nsec, - .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + - elem->value->vm_clock_nsec, - }; - - pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); - pstrcpy(sn.name, sizeof(sn.name), elem->value->name); - bdrv_snapshot_dump(func_fprintf, f, &sn); - func_fprintf(f, "\n"); - } - } - - if (info->has_format_specific) { - func_fprintf(f, "Format specific information:\n"); - bdrv_image_info_specific_dump(func_fprintf, f, info->format_specific); - } -} diff --git a/hmp.c b/hmp.c index b2a2b1f84e..bdd69d2c37 100644 --- a/hmp.c +++ b/hmp.c @@ -45,7 +45,7 @@ #include "qom/object_interfaces.h" #include "ui/console.h" #include "block/nbd.h" -#include "block/qapi.h" +#include "block/bdrv_info.h" #include "qemu-io.h" #include "qemu/cutils.h" #include "qemu/error-report.h" diff --git a/include/block/qapi.h b/include/block/bdrv_info.h similarity index 97% rename from include/block/qapi.h rename to include/block/bdrv_info.h index 83bdb098bd..6ba1441177 100644 --- a/include/block/qapi.h +++ b/include/block/bdrv_info.h @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#ifndef BLOCK_QAPI_H -#define BLOCK_QAPI_H +#ifndef BDRV_INFO_H +#define BDRV_INFO_H #include "block/block.h" #include "block/snapshot.h" @@ -43,4 +43,4 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, ImageInfoSpecific *info_spec); void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, ImageInfo *info); -#endif +#endif // BDRV_INFO_H diff --git a/monitor.c b/monitor.c index c09fa63940..edde1c0331 100644 --- a/monitor.c +++ b/monitor.c @@ -73,7 +73,7 @@ #include "qemu/option.h" #include "hmp.h" #include "qemu/thread.h" -#include "block/qapi.h" +#include "block/bdrv_info.h" #include "qapi/qapi-commands.h" #include "qapi/qapi-events.h" #include "qapi/error.h" diff --git a/qemu-img.c b/qemu-img.c index ad04f59565..61a5740ecc 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -42,7 +42,7 @@ #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" -#include "block/qapi.h" +#include "block/bdrv_info.h" #include "crypto/init.h" #include "trace/control.h" diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 2c39124036..72d8e82507 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -15,7 +15,7 @@ #include "sysemu/block-backend.h" #include "block/block.h" #include "block/block_int.h" /* for info_f() */ -#include "block/qapi.h" +#include "block/bdrv_info.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/option.h"