[PATCH v2 0/4] bhyve: implement domain{Block,Interface,Memory}Stats

Changes since v1: - Added "bhyve: implement domainInterfaceStats" patch PS It was temping to factor out obtaining struct kinfo_proc using sysctlnametomib() + sysctl(), but I have to make it visible to use outside of virprocess, e.g. in bhyve_driver.c, so it doesn't look like it's worth the effort for now. Probably it'll make sense to implement a FreeBSD version of virProcessGetStat() once there are more users of this code. Roman Bogorodskiy (4): bhyve: implement domainInterfaceStats virprocess: implement virProcessGetStatInfo() for FreeBSD bhyve: implement domainMemoryStats bhyve: implement domainBlockStats src/bhyve/bhyve_driver.c | 140 +++++++++++++++++++++++++++++++++++++++ src/util/virprocess.c | 104 +++++++++++++++++++++-------- 2 files changed, 218 insertions(+), 26 deletions(-) -- 2.49.0

The virNetDevTapInterfaceStats() function already works on FreeBSD, so it's just a matter of wrapping that for domainInterfaceStats. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_driver.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index db7d440a97..e4698b71bf 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -55,6 +55,7 @@ #include "conf/domain_capabilities.h" #include "virutil.h" #include "domain_driver.h" +#include "virnetdevtap.h" #include "bhyve_conf.h" #include "bhyve_device.h" @@ -1625,6 +1626,38 @@ bhyveConnectGetDomainCapabilities(virConnectPtr conn, return ret; } +static int +bhyveDomainInterfaceStats(virDomainPtr domain, + const char *device, + virDomainInterfaceStatsPtr stats) +{ + virDomainObj *vm; + int ret = -1; + virDomainNetDef *net = NULL; + + if (!(vm = bhyveDomObjFromDomain(domain))) + goto cleanup; + + if (virDomainInterfaceStatsEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto cleanup; + + if (!(net = virDomainNetFind(vm->def, device))) + goto cleanup; + + if (virNetDevTapInterfaceStats(net->ifname, stats, + !virDomainNetTypeSharesHostView(net)) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + static virHypervisorDriver bhyveHypervisorDriver = { .name = "bhyve", .connectURIProbe = bhyveConnectURIProbe, @@ -1685,6 +1718,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .connectIsEncrypted = bhyveConnectIsEncrypted, /* 1.3.5 */ .connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.1.0 */ .connectGetDomainCapabilities = bhyveConnectGetDomainCapabilities, /* 2.1.0 */ + .domainInterfaceStats = bhyveDomainInterfaceStats, /* 11.5.0 */ }; -- 2.49.0

On 6/1/25 07:40, Roman Bogorodskiy wrote:
The virNetDevTapInterfaceStats() function already works on FreeBSD, so it's just a matter of wrapping that for domainInterfaceStats.
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_driver.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index db7d440a97..e4698b71bf 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -55,6 +55,7 @@ #include "conf/domain_capabilities.h" #include "virutil.h" #include "domain_driver.h" +#include "virnetdevtap.h"
#include "bhyve_conf.h" #include "bhyve_device.h" @@ -1625,6 +1626,38 @@ bhyveConnectGetDomainCapabilities(virConnectPtr conn, return ret; }
+static int +bhyveDomainInterfaceStats(virDomainPtr domain, + const char *device, + virDomainInterfaceStatsPtr stats) +{ + virDomainObj *vm; + int ret = -1; + virDomainNetDef *net = NULL; + + if (!(vm = bhyveDomObjFromDomain(domain))) + goto cleanup; + + if (virDomainInterfaceStatsEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto cleanup; + + if (!(net = virDomainNetFind(vm->def, device))) + goto cleanup; + + if (virNetDevTapInterfaceStats(net->ifname, stats, + !virDomainNetTypeSharesHostView(net)) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + static virHypervisorDriver bhyveHypervisorDriver = { .name = "bhyve", .connectURIProbe = bhyveConnectURIProbe, @@ -1685,6 +1718,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .connectIsEncrypted = bhyveConnectIsEncrypted, /* 1.3.5 */ .connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.1.0 */ .connectGetDomainCapabilities = bhyveConnectGetDomainCapabilities, /* 2.1.0 */ + .domainInterfaceStats = bhyveDomainInterfaceStats, /* 11.5.0 */
This has to be 11.7.0 now; Sorry for letting this slip review this long.
};
Michal

Use the "kern.proc.pid" sysctl and retrieve information from the kinfo_proc struct. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/util/virprocess.c | 104 +++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 26 deletions(-) diff --git a/src/util/virprocess.c b/src/util/virprocess.c index 05db76d834..3889ba90f9 100644 --- a/src/util/virprocess.c +++ b/src/util/virprocess.c @@ -1812,7 +1812,85 @@ virProcessGetStatInfo(unsigned long long *cpuTime, return 0; } +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +int +virProcessGetStatInfo(unsigned long long *cpuTime, + unsigned long long *userTime, + unsigned long long *sysTime, + int *lastCpu, + unsigned long long *vm_rss, + pid_t pid, + pid_t tid) +{ + struct kinfo_proc p; + int mib[4]; + size_t len = 4; + unsigned long long utime = 0; + unsigned long long stime = 0; + unsigned long long rss = 0; + int cpu = 0; + const long pagesize = virGetSystemPageSizeKB(); + + sysctlnametomib("kern.proc.pid", mib, &len); + + len = sizeof(struct kinfo_proc); + mib[3] = pid; + + if (sysctl(mib, 4, &p, &len, NULL, 0) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query process stats")); + return -1; + } + + utime = p.ki_rusage.ru_utime.tv_sec; + stime = p.ki_rusage.ru_stime.tv_sec; + rss = p.ki_rssize * pagesize; + cpu = p.ki_lastcpu_old; + + if (cpuTime) + *cpuTime = utime + stime; + if (userTime) + *userTime = utime; + if (sysTime) + *sysTime = stime; + if (lastCpu) + *lastCpu = cpu; + if (vm_rss) + *vm_rss = rss; + + VIR_DEBUG("Got status for %d/%d user=%llu sys=%llu cpu=%d rss=%lld", + (int) pid, tid, utime, stime, cpu, rss); + + return 0; +} +#else +int +virProcessGetStatInfo(unsigned long long *cpuTime, + unsigned long long *userTime, + unsigned long long *sysTime, + int *lastCpu, + unsigned long long *vm_rss, + pid_t pid G_GNUC_UNUSED, + pid_t tid G_GNUC_UNUSED) +{ + /* We don't have a way to collect this information on non-Linux + * platforms, so just report neutral values */ + if (cpuTime) + *cpuTime = 0; + if (userTime) + *userTime = 0; + if (sysTime) + *sysTime = 0; + if (lastCpu) + *lastCpu = 0; + if (vm_rss) + *vm_rss = 0; + + return 0; +} +#endif +#ifdef __linux__ int virProcessGetSchedInfo(unsigned long long *cpuWait, pid_t pid, @@ -1879,33 +1957,7 @@ virProcessGetSchedInfo(unsigned long long *cpuWait, return 0; } - #else -int -virProcessGetStatInfo(unsigned long long *cpuTime, - unsigned long long *userTime, - unsigned long long *sysTime, - int *lastCpu, - unsigned long long *vm_rss, - pid_t pid G_GNUC_UNUSED, - pid_t tid G_GNUC_UNUSED) -{ - /* We don't have a way to collect this information on non-Linux - * platforms, so just report neutral values */ - if (cpuTime) - *cpuTime = 0; - if (userTime) - *userTime = 0; - if (sysTime) - *sysTime = 0; - if (lastCpu) - *lastCpu = 0; - if (vm_rss) - *vm_rss = 0; - - return 0; -} - int virProcessGetSchedInfo(unsigned long long *cpuWait, pid_t pid G_GNUC_UNUSED, -- 2.49.0

Currently, bhyve does not support neither memory ballooning nor reporting guest memory usage. So the following information can be obtained: - RSS of the running process - Memory available to the guest (that is, guest total memory) Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index e4698b71bf..70e28c7e0b 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -3,6 +3,7 @@ * * Copyright (C) 2014 Roman Bogorodskiy * Copyright (C) 2014-2015 Red Hat, Inc. + * Copyright (C) 2025 The FreeBSD Foundation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1658,6 +1659,54 @@ bhyveDomainInterfaceStats(virDomainPtr domain, return ret; } +#define BHYVE_SET_MEMSTAT(TAG, VAL) \ + if (i < nr_stats) { \ + stats[i].tag = TAG; \ + stats[i].val = VAL; \ + i++; \ + } + +static int +bhyveDomainMemoryStats(virDomainPtr domain, + virDomainMemoryStatPtr stats, + unsigned int nr_stats, + unsigned int flags) +{ + virDomainObj *vm; + unsigned maxmem; + unsigned long long rss; + size_t i = 0; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = bhyveDomObjFromDomain(domain))) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto cleanup; + + if (virDomainMemoryStatsEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + if (virProcessGetStatInfo(NULL, NULL, NULL, NULL, &rss, vm->pid, 0) < 0) + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("cannot get RSS for domain")); + else + BHYVE_SET_MEMSTAT(VIR_DOMAIN_MEMORY_STAT_RSS, rss); + + maxmem = virDomainDefGetMemoryTotal(vm->def); + BHYVE_SET_MEMSTAT(VIR_DOMAIN_MEMORY_STAT_AVAILABLE, maxmem); + + ret = i; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +#undef BHYVE_SET_MEMSTAT + static virHypervisorDriver bhyveHypervisorDriver = { .name = "bhyve", .connectURIProbe = bhyveConnectURIProbe, @@ -1719,6 +1768,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.1.0 */ .connectGetDomainCapabilities = bhyveConnectGetDomainCapabilities, /* 2.1.0 */ .domainInterfaceStats = bhyveDomainInterfaceStats, /* 11.5.0 */ + .domainMemoryStats = bhyveDomainMemoryStats, /* 11.5.0 */ }; -- 2.49.0

On 6/1/25 07:40, Roman Bogorodskiy wrote:
Currently, bhyve does not support neither memory ballooning nor reporting guest memory usage. So the following information can be obtained:
- RSS of the running process - Memory available to the guest (that is, guest total memory)
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index e4698b71bf..70e28c7e0b 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -3,6 +3,7 @@ * * Copyright (C) 2014 Roman Bogorodskiy * Copyright (C) 2014-2015 Red Hat, Inc. + * Copyright (C) 2025 The FreeBSD Foundation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1658,6 +1659,54 @@ bhyveDomainInterfaceStats(virDomainPtr domain, return ret; }
+#define BHYVE_SET_MEMSTAT(TAG, VAL) \ + if (i < nr_stats) { \ + stats[i].tag = TAG; \ + stats[i].val = VAL; \ + i++; \ + } + +static int +bhyveDomainMemoryStats(virDomainPtr domain, + virDomainMemoryStatPtr stats, + unsigned int nr_stats, + unsigned int flags) +{ + virDomainObj *vm; + unsigned maxmem; + unsigned long long rss; + size_t i = 0; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = bhyveDomObjFromDomain(domain))) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto cleanup; + + if (virDomainMemoryStatsEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + if (virProcessGetStatInfo(NULL, NULL, NULL, NULL, &rss, vm->pid, 0) < 0) + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("cannot get RSS for domain")); + else + BHYVE_SET_MEMSTAT(VIR_DOMAIN_MEMORY_STAT_RSS, rss);
Nitpick. If a body of a statement is longer than one line, then it has to be wrapped in curly braces. Then, if one body of an if() statement has curly braces then the other should have them too. TLDR - wrap both bodies into {}.
+ + maxmem = virDomainDefGetMemoryTotal(vm->def); + BHYVE_SET_MEMSTAT(VIR_DOMAIN_MEMORY_STAT_AVAILABLE, maxmem); + + ret = i; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +#undef BHYVE_SET_MEMSTAT + static virHypervisorDriver bhyveHypervisorDriver = { .name = "bhyve", .connectURIProbe = bhyveConnectURIProbe, @@ -1719,6 +1768,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.1.0 */ .connectGetDomainCapabilities = bhyveConnectGetDomainCapabilities, /* 2.1.0 */ .domainInterfaceStats = bhyveDomainInterfaceStats, /* 11.5.0 */ + .domainMemoryStats = bhyveDomainMemoryStats, /* 11.5.0 */
11.7.0
};
Michal

Implement domainBlockStats for the bhyve driver. Only the read/write operations counts are reported as FreeBSD apparently doesn't support accumulative bytes read or written, though real-time data is available via rctl(8). There's also no information about the errors. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_driver.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index 70e28c7e0b..acec98d100 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -24,6 +24,9 @@ #include <fcntl.h> #include <sys/utsname.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/user.h> #include "virerror.h" #include "datatypes.h" @@ -1707,6 +1710,58 @@ bhyveDomainMemoryStats(virDomainPtr domain, #undef BHYVE_SET_MEMSTAT +static struct kinfo_proc *bhyveDomainProcGetInfo(pid_t pid) +{ + int mib[4]; + size_t len = 4; + struct kinfo_proc *p = g_malloc0(sizeof(struct kinfo_proc)); + + sysctlnametomib("kern.proc.pid", mib, &len); + len = sizeof(struct kinfo_proc); + mib[3] = pid; + + if (sysctl(mib, 4, p, &len, NULL, 0) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query process stats")); + return NULL; + } + + return p; +} + +static int +bhyveDomainBlockStats(virDomainPtr domain, + const char *path G_GNUC_UNUSED, + virDomainBlockStatsPtr stats) +{ + virDomainObj *vm; + int ret = -1; + g_autofree struct kinfo_proc *p = NULL; + + if (!(vm = bhyveDomObjFromDomain(domain))) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto cleanup; + + if (virDomainBlockStatsEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + if ((p = bhyveDomainProcGetInfo(vm->pid)) == NULL) + goto cleanup; + + stats->rd_req = p->ki_rusage.ru_inblock; + stats->wr_req = p->ki_rusage.ru_oublock; + stats->rd_bytes = -1; + stats->wr_bytes = -1; + stats->errs = -1; + + ret = 0; + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + static virHypervisorDriver bhyveHypervisorDriver = { .name = "bhyve", .connectURIProbe = bhyveConnectURIProbe, @@ -1769,6 +1824,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .connectGetDomainCapabilities = bhyveConnectGetDomainCapabilities, /* 2.1.0 */ .domainInterfaceStats = bhyveDomainInterfaceStats, /* 11.5.0 */ .domainMemoryStats = bhyveDomainMemoryStats, /* 11.5.0 */ + .domainBlockStats = bhyveDomainBlockStats, /* 11.5.0 */ }; -- 2.49.0

Roman Bogorodskiy wrote:
Changes since v1:
- Added "bhyve: implement domainInterfaceStats" patch
PS It was temping to factor out obtaining struct kinfo_proc using sysctlnametomib() + sysctl(), but I have to make it visible to use outside of virprocess, e.g. in bhyve_driver.c, so it doesn't look like it's worth the effort for now. Probably it'll make sense to implement a FreeBSD version of virProcessGetStat() once there are more users of this code.
Roman Bogorodskiy (4): bhyve: implement domainInterfaceStats virprocess: implement virProcessGetStatInfo() for FreeBSD bhyve: implement domainMemoryStats bhyve: implement domainBlockStats
src/bhyve/bhyve_driver.c | 140 +++++++++++++++++++++++++++++++++++++++ src/util/virprocess.c | 104 +++++++++++++++++++++-------- 2 files changed, 218 insertions(+), 26 deletions(-)
ping

On 6/1/25 07:40, Roman Bogorodskiy wrote:
Changes since v1:
- Added "bhyve: implement domainInterfaceStats" patch
PS It was temping to factor out obtaining struct kinfo_proc using sysctlnametomib() + sysctl(), but I have to make it visible to use outside of virprocess, e.g. in bhyve_driver.c, so it doesn't look like it's worth the effort for now. Probably it'll make sense to implement a FreeBSD version of virProcessGetStat() once there are more users of this code.
Roman Bogorodskiy (4): bhyve: implement domainInterfaceStats virprocess: implement virProcessGetStatInfo() for FreeBSD bhyve: implement domainMemoryStats bhyve: implement domainBlockStats
src/bhyve/bhyve_driver.c | 140 +++++++++++++++++++++++++++++++++++++++ src/util/virprocess.c | 104 +++++++++++++++++++++-------- 2 files changed, 218 insertions(+), 26 deletions(-)
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> and sorry for letting this slip review this long. Michal
participants (1)
-
Michal Prívozník
-
Roman Bogorodskiy