[libvirt PATCH 0/3] cgroup cpu.shares cleanup

After the conversion to using systemd DBus APIs the behavior of changed for values outside of valid range. This series fixes the issue by enforcing the range on libvirt side instead of the previous magic conversion done by kernel. Pavel Hrdina (3): vircgroup: enforce range limit for cpu.shares cgroup: use virCgroupSetCpuShares instead of virCgroupSetupCpuShares vircgroup: drop unused function virCgroupSetupCpuShares docs/formatdomain.rst | 3 ++- docs/manpages/virsh.rst | 5 +---- src/conf/domain_validate.c | 10 ++++++++++ src/libvirt_private.syms | 1 - src/lxc/lxc_cgroup.c | 4 +--- src/lxc/lxc_driver.c | 6 ++---- src/qemu/qemu_cgroup.c | 21 +-------------------- src/qemu/qemu_driver.c | 7 +++---- src/util/vircgroup.c | 20 -------------------- src/util/vircgroup.h | 4 ++-- src/util/vircgroupv1.c | 10 ++++++++++ src/util/vircgroupv2.c | 10 ++++++++++ 12 files changed, 42 insertions(+), 59 deletions(-) -- 2.29.2

Before the conversion to using systemd DBus API to set the cpu.shares there was some magic conversion done by kernel which was documented in virsh manpage as well. Now systemd errors out if the value is out of range. Since we enforce the range for other cpu cgroup attributes 'quota' and 'period' it makes sense to do the same for 'shares' as well. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- docs/formatdomain.rst | 3 ++- docs/manpages/virsh.rst | 5 +---- src/conf/domain_validate.c | 10 ++++++++++ src/util/vircgroup.h | 2 ++ src/util/vircgroupv1.c | 10 ++++++++++ src/util/vircgroupv2.c | 10 ++++++++++ 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 937b0da012..b434ada8f0 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -742,7 +742,8 @@ CPU Tuning the domain. If this is omitted, it defaults to the OS provided defaults. NB, There is no unit for the value, it's a relative measure based on the setting of other VM, e.g. A VM configured with value 2048 will get twice as much CPU - time as a VM configured with value 1024. :since:`Since 0.9.0` + time as a VM configured with value 1024. The value should be in range + [2, 262144]. :since:`Since 0.9.0` ``period`` The optional ``period`` element specifies the enforcement interval (unit: microseconds). Within ``period``, each vCPU of the domain will not be allowed diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 8a4328faa0..d44c36e2e9 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -3807,10 +3807,7 @@ If *--config* is specified, affect the next start of a persistent guest. If *--current* is specified, it is equivalent to either *--live* or *--config*, depending on the current state of the guest. -``Note``: The cpu_shares parameter has a valid value range of 0-262144; Negative -values are wrapped to positive, and larger values are capped at the maximum. -Therefore, -1 is a useful shorthand for 262144. On the Linux kernel, the -values 0 and 1 are automatically converted to a minimal value of 2. +``Note``: The cpu_shares parameter has a valid value range of 2-262144. ``Note``: The weight and cap parameters are defined only for the XEN_CREDIT scheduler. diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index b4e09e21fe..61cd0a07e5 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1229,6 +1229,16 @@ virDomainDefOSValidate(const virDomainDef *def, static int virDomainDefCputuneValidate(const virDomainDef *def) { + if (def->cputune.shares > 0 && + (def->cputune.shares < VIR_CGROUP_CPU_SHARES_MIN || + def->cputune.shares > VIR_CGROUP_CPU_SHARES_MAX)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Value of cputune 'shares' must be in range [%llu, %llu]"), + VIR_CGROUP_CPU_SHARES_MIN, + VIR_CGROUP_CPU_SHARES_MAX); + return -1; + } + CPUTUNE_VALIDATE_PERIOD(period); CPUTUNE_VALIDATE_PERIOD(global_period); CPUTUNE_VALIDATE_PERIOD(emulator_period); diff --git a/src/util/vircgroup.h b/src/util/vircgroup.h index ec0902e301..7d9172d664 100644 --- a/src/util/vircgroup.h +++ b/src/util/vircgroup.h @@ -230,6 +230,8 @@ int virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares); int virCgroupSetupCpuShares(virCgroupPtr cgroup, unsigned long long shares, unsigned long long *realValue); +#define VIR_CGROUP_CPU_SHARES_MIN 2LL +#define VIR_CGROUP_CPU_SHARES_MAX 262144LL #define VIR_CGROUP_CPU_PERIOD_MIN 1000LL #define VIR_CGROUP_CPU_PERIOD_MAX 1000000LL #define VIR_CGROUP_CPU_QUOTA_MIN 1000LL diff --git a/src/util/vircgroupv1.c b/src/util/vircgroupv1.c index 79015ddaa4..f8bdca4e78 100644 --- a/src/util/vircgroupv1.c +++ b/src/util/vircgroupv1.c @@ -1917,6 +1917,16 @@ static int virCgroupV1SetCpuShares(virCgroupPtr group, unsigned long long shares) { + if (shares < VIR_CGROUP_CPU_SHARES_MIN || + shares > VIR_CGROUP_CPU_SHARES_MAX) { + virReportError(VIR_ERR_INVALID_ARG, + _("shares '%llu' must be in range [%llu, %llu]"), + shares, + VIR_CGROUP_CPU_SHARES_MIN, + VIR_CGROUP_CPU_SHARES_MAX); + return -1; + } + if (group->unitName) { GVariant *value = g_variant_new("t", shares); diff --git a/src/util/vircgroupv2.c b/src/util/vircgroupv2.c index 24c7e64c67..669bf1b52e 100644 --- a/src/util/vircgroupv2.c +++ b/src/util/vircgroupv2.c @@ -1505,6 +1505,16 @@ static int virCgroupV2SetCpuShares(virCgroupPtr group, unsigned long long shares) { + if (shares < VIR_CGROUP_CPU_SHARES_MIN || + shares > VIR_CGROUP_CPU_SHARES_MAX) { + virReportError(VIR_ERR_INVALID_ARG, + _("shares '%llu' must be in range [%llu, %llu]"), + shares, + VIR_CGROUP_CPU_SHARES_MIN, + VIR_CGROUP_CPU_SHARES_MAX); + return -1; + } + if (group->unitName) { GVariant *value = g_variant_new("t", shares); -- 2.29.2

Now that we enforce the cpu.shares range kernel will no longer silently change the value that libvirt configures so there is no need to read the value back to get the actual configuration. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/lxc/lxc_cgroup.c | 4 +--- src/lxc/lxc_driver.c | 6 ++---- src/qemu/qemu_cgroup.c | 21 +-------------------- src/qemu/qemu_driver.c | 7 +++---- 4 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 3c546861f1..af4322d6cf 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -39,10 +39,8 @@ static int virLXCCgroupSetupCpuTune(virDomainDefPtr def, virCgroupPtr cgroup) { if (def->cputune.sharesSpecified) { - unsigned long long val; - if (virCgroupSetupCpuShares(cgroup, def->cputune.shares, &val) < 0) + if (virCgroupSetCpuShares(cgroup, def->cputune.shares) < 0) return -1; - def->cputune.shares = val; } return virCgroupSetupCpuPeriodQuota(cgroup, def->cputune.period, diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 4416acf923..61faceb2de 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1868,12 +1868,10 @@ lxcDomainSetSchedulerParametersFlags(virDomainPtr dom, if (STREQ(param->field, VIR_DOMAIN_SCHEDULER_CPU_SHARES)) { if (def) { - unsigned long long val; - if (virCgroupSetupCpuShares(priv->cgroup, params[i].value.ul, - &val) < 0) + if (virCgroupSetCpuShares(priv->cgroup, params[i].value.ul) < 0) goto endjob; - def->cputune.shares = val; + def->cputune.shares = params[i].value.ul; def->cputune.sharesSpecified = true; } diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 8c31bb4210..4d94b57e57 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -880,10 +880,6 @@ static int qemuSetupCpuCgroup(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event = NULL; - virTypedParameterPtr eventParams = NULL; - int eventNparams = 0; - int eventMaxparams = 0; if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPU)) { if (vm->def->cputune.sharesSpecified) { @@ -896,23 +892,8 @@ qemuSetupCpuCgroup(virDomainObjPtr vm) } if (vm->def->cputune.sharesSpecified) { - unsigned long long val; - if (virCgroupSetupCpuShares(priv->cgroup, vm->def->cputune.shares, - &val) < 0) + if (virCgroupSetCpuShares(priv->cgroup, vm->def->cputune.shares) < 0) return -1; - - if (vm->def->cputune.shares != val) { - vm->def->cputune.shares = val; - if (virTypedParamsAddULLong(&eventParams, &eventNparams, - &eventMaxparams, - VIR_DOMAIN_TUNABLE_CPU_CPU_SHARES, - val) < 0) - return -1; - - event = virDomainEventTunableNewFromObj(vm, eventParams, eventNparams); - } - - virObjectEventStateQueue(priv->driver->domainEventState, event); } return 0; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d1a3659774..9a6934f30f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9324,17 +9324,16 @@ qemuDomainSetSchedulerParametersFlags(virDomainPtr dom, if (STREQ(param->field, VIR_DOMAIN_SCHEDULER_CPU_SHARES)) { if (def) { - unsigned long long val; - if (virCgroupSetupCpuShares(priv->cgroup, value_ul, &val) < 0) + if (virCgroupSetCpuShares(priv->cgroup, value_ul) < 0) goto endjob; - def->cputune.shares = val; + def->cputune.shares = value_ul; def->cputune.sharesSpecified = true; if (virTypedParamsAddULLong(&eventParams, &eventNparams, &eventMaxNparams, VIR_DOMAIN_TUNABLE_CPU_CPU_SHARES, - val) < 0) + value_ul) < 0) goto endjob; } -- 2.29.2

Previous commit removed all usage of this function so we can remove it. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/util/vircgroup.c | 20 -------------------- src/util/vircgroup.h | 2 -- 3 files changed, 23 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2a3bbdc577..3e7de7744e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1934,7 +1934,6 @@ virCgroupSetupBlkioDeviceWriteBps; virCgroupSetupBlkioDeviceWriteIops; virCgroupSetupCpuPeriodQuota; virCgroupSetupCpusetCpus; -virCgroupSetupCpuShares; virCgroupSupportsCpuBW; virCgroupTerminateMachine; diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c index abc06b8cb0..5b6097c335 100644 --- a/src/util/vircgroup.c +++ b/src/util/vircgroup.c @@ -3899,26 +3899,6 @@ virCgroupSetupCpusetCpus(virCgroupPtr cgroup, virBitmapPtr cpumask) } -/* Per commit 97814d8ab3, the Linux kernel can consider a 'shares' - * value of '0' and '1' as 2, and any value larger than a maximum - * is reduced to maximum. - * - * The 'realValue' pointer holds the actual 'shares' value set by - * the kernel if the function returned success. */ -int -virCgroupSetupCpuShares(virCgroupPtr cgroup, unsigned long long shares, - unsigned long long *realValue) -{ - if (virCgroupSetCpuShares(cgroup, shares) < 0) - return -1; - - if (virCgroupGetCpuShares(cgroup, realValue) < 0) - return -1; - - return 0; -} - - int virCgroupSetupCpuPeriodQuota(virCgroupPtr cgroup, unsigned long long period, diff --git a/src/util/vircgroup.h b/src/util/vircgroup.h index 7d9172d664..4e6911f34c 100644 --- a/src/util/vircgroup.h +++ b/src/util/vircgroup.h @@ -227,8 +227,6 @@ virCgroupGetDomainTotalCpuStats(virCgroupPtr group, int virCgroupSetCpuShares(virCgroupPtr group, unsigned long long shares); int virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares); -int virCgroupSetupCpuShares(virCgroupPtr cgroup, unsigned long long shares, - unsigned long long *realValue); #define VIR_CGROUP_CPU_SHARES_MIN 2LL #define VIR_CGROUP_CPU_SHARES_MAX 262144LL -- 2.29.2

On 3/3/21 2:35 PM, Pavel Hrdina wrote:
After the conversion to using systemd DBus APIs the behavior of changed for values outside of valid range. This series fixes the issue by enforcing the range on libvirt side instead of the previous magic conversion done by kernel.
Pavel Hrdina (3): vircgroup: enforce range limit for cpu.shares cgroup: use virCgroupSetCpuShares instead of virCgroupSetupCpuShares vircgroup: drop unused function virCgroupSetupCpuShares
docs/formatdomain.rst | 3 ++- docs/manpages/virsh.rst | 5 +---- src/conf/domain_validate.c | 10 ++++++++++ src/libvirt_private.syms | 1 - src/lxc/lxc_cgroup.c | 4 +--- src/lxc/lxc_driver.c | 6 ++---- src/qemu/qemu_cgroup.c | 21 +-------------------- src/qemu/qemu_driver.c | 7 +++---- src/util/vircgroup.c | 20 -------------------- src/util/vircgroup.h | 4 ++-- src/util/vircgroupv1.c | 10 ++++++++++ src/util/vircgroupv2.c | 10 ++++++++++ 12 files changed, 42 insertions(+), 59 deletions(-)
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal
participants (2)
-
Michal Privoznik
-
Pavel Hrdina