On Tue, Apr 07, 2026 at 20:13:33 +0200, Roman Bogorodskiy wrote: > FreeBSD supports resource limiting with the rctl(4) framework. > It supports various resource types, including I/O resources. > It allows to limit resources for users, processes, login classes, > and jails. > > To apply blkiotune limits set limits for the bhyve process. > > I/O related resources supported by rctl(4) are: > > readbps filesystem reads, in bytes per second > writebps filesystem writes, in bytes per second > readiops filesystem reads, in operations per second > writeiops filesystem writes, in operations per second > > Thus, the actual commands look like: > > rctl -a process:$bhyvepid:writebps:throttle=10000000 > rctl -a process:$bhyvepid:readbps:throttle=10000000 > rctl -a process:$bhyvepid:writeiops:throttle=20000 > rctl -a process:$bhyvepid:readiops:throttle=20000 > > This is different from the current blkiotune modeling in libvirt as > it requires specific device to apply limits to. To adapt this model > to per-domain I/O limits, update domain schema to specify "*" as a > device name.
You've changed the schema and added examples, which is good, but didn't document this in formatdomain.rst. > > The rctl(8) may be not available or not enabled, so add a capability > check for that. > > Per process rules get removed when the process disappears, so no special > clean up is necessary. > > Signed-off-by: Roman Bogorodskiy <[email protected]> > --- > src/bhyve/bhyve_capabilities.c | 25 +++++++- > src/bhyve/bhyve_capabilities.h | 1 + > src/bhyve/bhyve_domain.c | 6 ++ > src/bhyve/bhyve_process.c | 59 +++++++++++++++++++ > src/conf/schemas/domaincommon.rng | 5 +- > ...yvexml2argv-blkiotune-multiple-devices.xml | 39 ++++++++++++ > .../x86_64/bhyvexml2argv-blkiotune.args | 10 ++++ > .../x86_64/bhyvexml2argv-blkiotune.ldargs | 4 ++ > .../x86_64/bhyvexml2argv-blkiotune.xml | 32 ++++++++++ > tests/bhyvexml2argvtest.c | 2 + > .../x86_64/bhyvexml2xmlout-blkiotune.xml | 42 +++++++++++++ > tests/bhyvexml2xmltest.c | 2 + > 12 files changed, 225 insertions(+), 2 deletions(-) > create mode 100644 > tests/bhyvexml2argvdata/x86_64/bhyvexml2argv-blkiotune-multiple-devices.xml > create mode 100644 > tests/bhyvexml2argvdata/x86_64/bhyvexml2argv-blkiotune.args > create mode 100644 > tests/bhyvexml2argvdata/x86_64/bhyvexml2argv-blkiotune.ldargs > create mode 100644 tests/bhyvexml2argvdata/x86_64/bhyvexml2argv-blkiotune.xml > create mode 100644 > tests/bhyvexml2xmloutdata/x86_64/bhyvexml2xmlout-blkiotune.xml > > diff --git a/src/bhyve/bhyve_capabilities.c b/src/bhyve/bhyve_capabilities.c > index c3fb88fe9f..1088d75407 100644 > --- a/src/bhyve/bhyve_capabilities.c > +++ b/src/bhyve/bhyve_capabilities.c > @@ -24,6 +24,7 @@ > #include <config.h> > #include <sys/utsname.h> > #include <dirent.h> > +#include <sys/sysctl.h> > #include <sys/types.h> > > #include "viralloc.h" > @@ -40,7 +41,6 @@ > > VIR_LOG_INIT("bhyve.bhyve_capabilities"); > > - > virCaps * > virBhyveCapsBuild(void) > { Spurious whithespace change. > @@ -334,6 +334,27 @@ bhyveProbeCapsVNCPassword(unsigned int *caps, char > *binary) > } > > > +static int > +bhyveProbeCapsRctl(unsigned int *caps) > +{ > + bool racct_enable; > + size_t racct_enable_len; > + g_autofree char *rctl = NULL; > + > + if (!(rctl = virFindFileInPath("rctl"))) > + return 0; > + > + racct_enable_len = sizeof(racct_enable); > + if (sysctlbyname("kern.racct.enable", &racct_enable, > + &racct_enable_len, NULL, 0) < 0) > + return 0; This returns 0 even on errors in this function. Change it to void if you don't plan to report errors. > + > + if (racct_enable) > + *caps |= BHYVE_CAP_RCTL; > + > + return 0; > +} > + > int > virBhyveProbeCaps(unsigned int *caps) > { > @@ -356,6 +377,8 @@ virBhyveProbeCaps(unsigned int *caps) > if ((ret = bhyveProbeCapsVNCPassword(caps, binary))) > goto out; > > + if ((ret = bhyveProbeCapsRctl(caps))) > + goto out; > > out: > VIR_FREE(binary); > diff --git a/src/bhyve/bhyve_capabilities.h b/src/bhyve/bhyve_capabilities.h > index 31fd9ab86a..0302b68e22 100644 > --- a/src/bhyve/bhyve_capabilities.h > +++ b/src/bhyve/bhyve_capabilities.h > @@ -57,6 +57,7 @@ typedef enum { > BHYVE_CAP_NVME = 1 << 11, > BHYVE_CAP_ACPI = 1 << 12, > BHYVE_CAP_NUMA = 1 << 13, > + BHYVE_CAP_RCTL = 1 << 14, > } virBhyveCapsFlags; > > int virBhyveProbeGrubCaps(virBhyveGrubCapsFlags *caps); > diff --git a/src/bhyve/bhyve_domain.c b/src/bhyve/bhyve_domain.c > index 4594d7673f..48e1451d8b 100644 > --- a/src/bhyve/bhyve_domain.c > +++ b/src/bhyve/bhyve_domain.c > @@ -464,6 +464,12 @@ bhyveDomainDefValidate(const virDomainDef *def, > } > } > > + if (def->blkio.ndevices > 1) { > + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", > + _("Per device I/O tuning is not supported")); > + return -1; > + } This validates only that you have just 1 "device" but doesn't validate that the single device has the special '*' path to mean all devices should be throttled. Users thus could spcify a single entry here with an actual path and have result which doesn't reflect the configuration. You also likely want to reject any 'weight' setting if this can't be honoured. > + > if (!def->os.loader) > return 0; > > diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c > index 1d436da609..b5ebe0aa61 100644 > --- a/src/bhyve/bhyve_process.c > +++ b/src/bhyve/bhyve_process.c > @@ -34,6 +34,7 @@ > > #include "bhyve_device.h" > #include "bhyve_driver.h" > +#include "bhyve_capabilities.h" > #include "bhyve_command.h" > #include "bhyve_firmware.h" > #include "bhyve_monitor.h" > @@ -132,6 +133,61 @@ bhyveProcessStopHook(struct _bhyveConn *driver, > VIR_HOOK_SUBOP_END, NULL, xml, NULL); > } > > +static int > +bhyveSetResourceLimits(struct _bhyveConn *driver, virDomainObj *vm) > +{ > + virBlkioDevice *device; > + char **rules = NULL; > + size_t i, nrules = 0; one decl per line > + int ret = -1; > + > + if (!vm->def->blkio.ndevices) 'ndevices' is a number, not a boolean/pointer. Use an explicit comparison against 0. > + return 0; > + > + if ((bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_RCTL) == 0) { > + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", > + _("Cannot set resource limits: RACCT/RCTL is either > not supported or not enabled")); > + return ret; > + } > + > + device = &vm->def->blkio.devices[0]; > + > +#define BHYVE_APPEND_RCTL_RULE(field, type, format) \ > + do { \ > + if ((field)) { \ > + virBuffer buf = VIR_BUFFER_INITIALIZER; \ > + char *rule; \ > + virBufferAsprintf(&buf, "process:%d:" type ":throttle=" format, \ > + vm->pid, (field)); \ > + rule = virBufferContentAndReset(&buf); \ > + VIR_APPEND_ELEMENT(rules, nrules, rule); \ > + } \ > + } while (0) > + > + BHYVE_APPEND_RCTL_RULE(device->riops, "readiops", "%u"); > + BHYVE_APPEND_RCTL_RULE(device->wiops, "writeiops", "%u"); > + BHYVE_APPEND_RCTL_RULE(device->rbps, "readbps", "%llu"); > + BHYVE_APPEND_RCTL_RULE(device->wbps, "writebps", "%llu"); > + > +#undef BHYVE_APPEND_RCTL_RULE > + > + for (i = 0; i < nrules; i++) { > + g_autoptr(virCommand) cmd = virCommandNew("rctl"); > + virCommandAddArgList(cmd, "-a", rules[i], NULL); > + > + if (virCommandRun(cmd, NULL) < 0) > + goto cleanup; Since this executes once per rule, you might as well as directly do this in the macro rather than costructing a list and then walking it to apply the rule. > + } > + > + ret = 0; > + > + cleanup: > + for (i = 0; i < nrules; i++) > + VIR_FREE(rules[i]); > + VIR_FREE(rules); > + return ret; > +} > + > static int > virBhyveProcessStartImpl(struct _bhyveConn *driver, > virDomainObj *vm, > @@ -258,6 +314,9 @@ virBhyveProcessStartImpl(struct _bhyveConn *driver, > BHYVE_STATE_DIR) < 0) > goto cleanup; > > + if (bhyveSetResourceLimits(driver, vm) < 0) > + goto cleanup; > + > if (bhyveProcessStartHook(driver, vm, VIR_HOOK_BHYVE_OP_STARTED) < 0) > goto cleanup; > > diff --git a/src/conf/schemas/domaincommon.rng > b/src/conf/schemas/domaincommon.rng > index db1dcd3bb7..c7f442a4c1 100644 > --- a/src/conf/schemas/domaincommon.rng > +++ b/src/conf/schemas/domaincommon.rng > @@ -1049,7 +1049,10 @@ > <element name="device"> > <interleave> > <element name="path"> > - <ref name="absFilePath"/> > + <choice> > + <ref name="absFilePath"/> > + <value>*</value> > + </choice> > </element> > <optional> > <element name="weight">
