diff --git a/src/shared/cgroup-setup.c b/src/shared/cgroup-setup.c index 7dee7e95127..aa5ee99ebfa 100644 --- a/src/shared/cgroup-setup.c +++ b/src/shared/cgroup-setup.c @@ -17,6 +17,63 @@ #include "stdio-util.h" #include "string-util.h" #include "user-util.h" +#include "virt.h" + +static int cg_any_controller_used_for_v1(void) { + _cleanup_free_ char *buf = NULL; + _cleanup_strv_free_ char **lines = NULL; + char **line; + int r; + + r = read_full_virtual_file("/proc/cgroups", &buf, NULL); + if (r < 0) + return log_debug_errno(r, "Could not read /proc/cgroups, ignoring: %m"); + + r = strv_split_newlines_full(&lines, buf, 0); + if (r < 0) + return r; + + /* The intention of this is to check if the fully unified cgroup tree setup is possible, meaning all + * enabled kernel cgroup controllers are currently not in use by cgroup1. For reference: + * https://systemd.io/CGROUP_DELEGATION/#three-different-tree-setups- + * + * Note that this is typically only useful to check inside a container where we don't know what + * cgroup tree setup is in use by the host; if the host is using legacy or hybrid, we can't use + * unified since some or all controllers would be missing. This is not the best way to detect this, + * as whatever container manager created our container should have mounted /sys/fs/cgroup + * appropriately, but in case that wasn't done, we try to detect if it's possible for us to use + * unified cgroups. */ + STRV_FOREACH(line, lines) { + _cleanup_free_ char *name = NULL, *hierarchy_id = NULL, *num = NULL, *enabled = NULL; + + /* Skip header line */ + if (startswith(*line, "#")) + continue; + + const char *p = *line; + r = extract_many_words(&p, NULL, 0, &name, &hierarchy_id, &num, &enabled, NULL); + if (r < 0) + return log_debug_errno(r, "Error parsing /proc/cgroups line, ignoring: %m"); + else if (r < 4) { + log_debug("Invalid /proc/cgroups line, ignoring."); + continue; + } + + /* Ignore disabled controllers. */ + if (streq(enabled, "0")) + continue; + + /* Since the unified cgroup doesn't use multiple hierarchies, if any controller has a + * non-zero hierarchy_id that means it's in use already in a legacy (or hybrid) cgroup v1 + * hierarchy, and can't be used in a unified cgroup. */ + if (!streq(hierarchy_id, "0")) { + log_debug("Cgroup controller %s in use by legacy v1 hierarchy.", name); + return 1; + } + } + + return 0; +} bool cg_is_unified_wanted(void) { static thread_local int wanted = -1; @@ -45,6 +102,10 @@ bool cg_is_unified_wanted(void) { if (r > 0 && streq_ptr(c, "all")) return (wanted = true); + /* If any controller is in use as v1, don't use unified. */ + if (cg_any_controller_used_for_v1() > 0) + return (wanted = false); + return (wanted = is_default); }