[PATCH 0/3] bhyve: implement domainInterfaceStats and domainMemoryStats

Roman Bogorodskiy (3): bhyve: implement domainInterfaceStats virprocess: implement virProcessGetStatInfo() for FreeBSD bhyve: implement domainMemoryStats src/bhyve/bhyve_driver.c | 84 +++++++++++++++++++++++++++++++ src/util/virprocess.c | 104 +++++++++++++++++++++++++++++---------- 2 files changed, 162 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

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
participants (1)
-
Roman Bogorodskiy