Reviewed-by: Kostiantyn Kostiuk <[email protected]> On Tue, Sep 1, 2026 at 10:29 PM Roman Bogorodskiy <[email protected]> wrote:
> Implement the 'guest-get-diskstats' for FreeBSD. > This implementation uses the devstat(3) library which is a part > of the FreeBSD base system. > > The build system is updated to link qga with `-ldevstat`. > > Signed-off-by: Roman Bogorodskiy <[email protected]> > Acked-by: Markus Armbruster <[email protected]> (QAPI schema) > --- > Changes since v1: > - Replace the guest_diskstats_append() helper with a simpler > guest_diskstats_info_new(). > - Drop the unnecessary condition for ios_pgr. > - Drop the unnecessary guest_get_diskstats() wrapper. > - Jump to error on devstat_getdevs() as it does allocations. > > qga/commands-bsd.c | 120 +++++++++++++++++++++++++++++++++++++++++++ > qga/meson.build | 3 ++ > qga/qapi-schema.json | 6 +-- > 3 files changed, 126 insertions(+), 3 deletions(-) > > diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c > index 94ff6fee6a..6a1aadee8d 100644 > --- a/qga/commands-bsd.c > +++ b/qga/commands-bsd.c > @@ -14,6 +14,7 @@ > #include "qga-qapi-commands.h" > #include "qapi/error.h" > #include "qemu/queue.h" > +#include "block/block-common.h" > #include "commands-common.h" > #include <sys/ioctl.h> > #include <sys/param.h> > @@ -27,6 +28,9 @@ > #include <net/ethernet.h> > #endif > #include <paths.h> > +#ifdef CONFIG_FREEBSD > +#include <devstat.h> > +#endif > > #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) > bool build_fs_mount_list(FsMountList *mounts, Error **errp) > @@ -178,3 +182,119 @@ bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned > char *buf, > return true; > } > #endif /* HAVE_GETIFADDRS */ > + > +#ifdef CONFIG_FREEBSD > +static uint64_t bintime_to_msec(const struct bintime *bt) > +{ > + return (uint64_t)bt->sec * 1000ULL + (((bt->frac >> 32) * 1000ULL) >> > 32); > +} > + > +static GuestDiskStatsInfo * > +guest_diskstats_info_new(const struct devstat *dev) > +{ > + GuestDiskStatsInfo *diskstatinfo; > + GuestDiskStats *diskstat; > + > + diskstatinfo = g_new0(GuestDiskStatsInfo, 1); > + diskstatinfo->name = g_strdup_printf("%s%d", dev->device_name, > + dev->unit_number); > + /* > + * devstat does not expose Linux-style major/minor numbers. Report > the > + * devstat device number and unit number in these mandatory QAPI > fields. > + */ > + diskstatinfo->major = dev->device_number; > + diskstatinfo->minor = dev->unit_number; > + > + diskstat = g_new0(GuestDiskStats, 1); > + diskstat->has_read_ios = true; > + diskstat->read_ios = dev->operations[DEVSTAT_READ]; > + diskstat->has_read_sectors = true; > + diskstat->read_sectors = dev->bytes[DEVSTAT_READ] / BDRV_SECTOR_SIZE; > + diskstat->has_read_ticks = true; > + diskstat->read_ticks = bintime_to_msec(&dev->duration[DEVSTAT_READ]); > + > + diskstat->has_write_ios = true; > + diskstat->write_ios = dev->operations[DEVSTAT_WRITE]; > + diskstat->has_write_sectors = true; > + diskstat->write_sectors = dev->bytes[DEVSTAT_WRITE] / > BDRV_SECTOR_SIZE; > + diskstat->has_write_ticks = true; > + diskstat->write_ticks = > bintime_to_msec(&dev->duration[DEVSTAT_WRITE]); > + > + diskstat->has_discard_ios = true; > + diskstat->discard_ios = dev->operations[DEVSTAT_FREE]; > + diskstat->has_discard_sectors = true; > + diskstat->discard_sectors = dev->bytes[DEVSTAT_FREE] / > BDRV_SECTOR_SIZE; > + diskstat->has_discard_ticks = true; > + diskstat->discard_ticks = > bintime_to_msec(&dev->duration[DEVSTAT_FREE]); > + > + diskstat->has_ios_pgr = true; > + diskstat->ios_pgr = dev->start_count - dev->end_count; > + > + diskstat->has_total_ticks = true; > + diskstat->total_ticks = bintime_to_msec(&dev->busy_time); > + > + diskstatinfo->stats = diskstat; > + return diskstatinfo; > +} > + > +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) > +{ > + GuestDiskStatsInfoList *head = NULL, **tail = &head; > + struct devinfo dinfo = { 0 }; > + struct statinfo stats = { .dinfo = &dinfo }; > + struct device_selection *dev_select = NULL; > + struct devstat_match matches[] = { > + { > + .match_fields = DEVSTAT_MATCH_TYPE, > + .device_type = DEVSTAT_TYPE_DIRECT, > + .num_match_categories = 1, > + }, > + }; > + int num_selected = 0; > + int num_selections = 0; > + long select_generation = 0; > + int i; > + > + if (devstat_checkversion(NULL) == -1) { > + error_setg(errp, "%s", devstat_errbuf); > + return NULL; > + } > + > + if (devstat_getdevs(NULL, &stats) == -1) { > + error_setg(errp, "%s", devstat_errbuf); > + goto error; > + } > + > + if (devstat_selectdevs(&dev_select, &num_selected, &num_selections, > + &select_generation, stats.dinfo->generation, > + stats.dinfo->devices, stats.dinfo->numdevs, > + matches, ARRAY_SIZE(matches), NULL, 0, > + DS_SELECT_ONLY, > + stats.dinfo->numdevs, 0) == -1) { > + error_setg(errp, "%s", devstat_errbuf); > + goto error; > + } > + > + for (i = 0; i < num_selections; i++) { > + const struct devstat *dev; > + > + if (dev_select[i].selected == 0) { > + continue; > + } > + > + dev = &stats.dinfo->devices[dev_select[i].position]; > + QAPI_LIST_APPEND(tail, guest_diskstats_info_new(dev)); > + } > + > + free(stats.dinfo->mem_ptr); > + free(dev_select); > + return head; > + > +error: > + qapi_free_GuestDiskStatsInfoList(head); > + free(stats.dinfo->mem_ptr); > + free(dev_select); > + return NULL; > +} > + > +#endif /* CONFIG_FREEBSD */ > diff --git a/qga/meson.build b/qga/meson.build > index cfa2157efb..a046ebe305 100644 > --- a/qga/meson.build > +++ b/qga/meson.build > @@ -93,6 +93,9 @@ qga_ss = qga_ss.apply({}) > > gen_tlb = [] > qga_libs = [] > +if host_os == 'freebsd' > + qga_libs += ['-ldevstat'] > +endif > if host_os == 'windows' > qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', > '-lwininet', '-liphlpapi', '-lnetapi32', > '-lsetupapi', '-lcfgmgr32', '-luserenv', '-lpdh' ] > diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json > index c57bc9a02f..0d1476dcc4 100644 > --- a/qga/qapi-schema.json > +++ b/qga/qapi-schema.json > @@ -1714,7 +1714,7 @@ > '*total-ticks': 'uint64', > '*weight-ticks': 'uint64' > }, > - 'if': 'CONFIG_LINUX' } > + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_FREEBSD'] } } > > ## > # @GuestDiskStatsInfo: > @@ -1732,7 +1732,7 @@ > 'major': 'uint64', > 'minor': 'uint64', > 'stats': 'GuestDiskStats' }, > - 'if': 'CONFIG_LINUX' } > + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_FREEBSD'] } } > > ## > # @guest-get-diskstats: > @@ -1745,7 +1745,7 @@ > ## > { 'command': 'guest-get-diskstats', > 'returns': ['GuestDiskStatsInfo'], > - 'if': 'CONFIG_LINUX' > + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_FREEBSD'] } > } > > ## > -- > 2.55.0 > >
