cgroup v2: Don't require CPU controller for CPU accounting in 4.15+

systemd only uses functions that are as of Linux 4.15+ provided
externally to the CPU controller (currently usage_usec), so if we have a
new enough kernel, we don't need to set CGROUP_MASK_CPU for
CPUAccounting=true as the CPU controller does not need to necessarily be
enabled in this case.

Part of this patch is modelled on an earlier patch by Ryutaroh Matsumoto
(see PR #9665).
This commit is contained in:
Chris Down
2018-11-18 12:21:41 +00:00
parent 4472fa6d2c
commit f98c25850f
6 changed files with 105 additions and 32 deletions
+52
View File
@@ -12,6 +12,7 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/xattr.h>
#include <unistd.h>
@@ -2822,3 +2823,54 @@ static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
CGroupMask get_cpu_accounting_mask(void) {
static CGroupMask needed_mask = (CGroupMask) -1;
/* On kernel ≥4.15 with unified hierarchy, cpu.stat's usage_usec is
* provided externally from the CPU controller, which means we don't
* need to enable the CPU controller just to get metrics. This is good,
* because enabling the CPU controller comes at a minor performance
* hit, especially when it's propagated deep into large hierarchies.
* There's also no separate CPU accounting controller available within
* a unified hierarchy.
*
* This combination of factors results in the desired cgroup mask to
* enable for CPU accounting varying as follows:
*
* ╔═════════════════════╤═════════════════════╗
* ║ Linux ≥4.15 │ Linux <4.15 ║
* ╔═══════════════╬═════════════════════╪═════════════════════╣
* ║ Unified ║ nothing │ CGROUP_MASK_CPU ║
* ╟───────────────╫─────────────────────┼─────────────────────╢
* ║ Hybrid/Legacy ║ CGROUP_MASK_CPUACCT │ CGROUP_MASK_CPUACCT ║
* ╚═══════════════╩═════════════════════╧═════════════════════╝
*
* We check kernel version here instead of manually checking whether
* cpu.stat is present for every cgroup, as that check in itself would
* already be fairly expensive.
*
* Kernels where this patch has been backported will therefore have the
* CPU controller enabled unnecessarily. This is more expensive than
* necessary, but harmless. ☺️
*/
if (needed_mask == (CGroupMask) -1) {
if (cg_all_unified()) {
struct utsname u;
assert_se(uname(&u) >= 0);
if (str_verscmp(u.release, "4.15") < 0)
needed_mask = CGROUP_MASK_CPU;
else
needed_mask = 0;
} else
needed_mask = CGROUP_MASK_CPUACCT;
}
return needed_mask;
}
bool cpu_accounting_is_cheap(void) {
return get_cpu_accounting_mask() == 0;
}