core: use the unified hierarchy for the systemd cgroup controller hierarchy

Currently, systemd uses either the legacy hierarchies or the unified hierarchy.
When the legacy hierarchies are used, systemd uses a named legacy hierarchy
mounted on /sys/fs/cgroup/systemd without any kernel controllers for process
management.  Due to the shortcomings in the legacy hierarchy, this involves a
lot of workarounds and complexities.

Because the unified hierarchy can be mounted and used in parallel to legacy
hierarchies, there's no reason for systemd to use a legacy hierarchy for
management even if the kernel resource controllers need to be mounted on legacy
hierarchies.  It can simply mount the unified hierarchy under
/sys/fs/cgroup/systemd and use it without affecting other legacy hierarchies.
This disables a significant amount of fragile workaround logics and would allow
using features which depend on the unified hierarchy membership such bpf cgroup
v2 membership test.  In time, this would also allow deleting the said
complexities.

This patch updates systemd so that it prefers the unified hierarchy for the
systemd cgroup controller hierarchy when legacy hierarchies are used for kernel
resource controllers.

* cg_unified(@controller) is introduced which tests whether the specific
  controller in on unified hierarchy and used to choose the unified hierarchy
  code path for process and service management when available.  Kernel
  controller specific operations remain gated by cg_all_unified().

* "systemd.legacy_systemd_cgroup_controller" kernel argument can be used to
  force the use of legacy hierarchy for systemd cgroup controller.

* nspawn: By default nspawn uses the same hierarchies as the host.  If
  UNIFIED_CGROUP_HIERARCHY is set to 1, unified hierarchy is used for all.  If
  0, legacy for all.

* nspawn: arg_unified_cgroup_hierarchy is made an enum and now encodes one of
  three options - legacy, only systemd controller on unified, and unified.  The
  value is passed into mount setup functions and controls cgroup configuration.

* nspawn: Interpretation of SYSTEMD_CGROUP_CONTROLLER to the actual mount
  option is moved to mount_legacy_cgroup_hierarchy() so that it can take an
  appropriate action depending on the configuration of the host.

v2: - CGroupUnified enum replaces open coded integer values to indicate the
      cgroup operation mode.
    - Various style updates.

v3: Fixed a bug in detect_unified_cgroup_hierarchy() introduced during v2.

v4: Restored legacy container on unified host support and fixed another bug in
    detect_unified_cgroup_hierarchy().
This commit is contained in:
Tejun Heo
2016-08-17 17:44:36 -04:00
committed by Tejun Heo
parent ca2f6384aa
commit 5da38d0768
13 changed files with 181 additions and 73 deletions
+90 -23
View File
@@ -869,7 +869,7 @@ int cg_set_task_access(
if (r < 0)
return r;
unified = cg_all_unified();
unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified)
@@ -893,18 +893,17 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
assert(path);
assert(pid >= 0);
unified = cg_all_unified();
if (controller) {
if (!cg_controller_is_valid(controller))
return -EINVAL;
} else
controller = SYSTEMD_CGROUP_CONTROLLER;
unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified == 0) {
if (controller) {
if (!cg_controller_is_valid(controller))
return -EINVAL;
} else
controller = SYSTEMD_CGROUP_CONTROLLER;
if (unified == 0)
cs = strlen(controller);
}
fs = procfs_file_alloca(pid, "cgroup");
f = fopen(fs, "re");
@@ -969,7 +968,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
assert(agent);
unified = cg_all_unified();
unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* doesn't apply to unified hierarchy */
@@ -1020,7 +1019,7 @@ int cg_uninstall_release_agent(const char *controller) {
_cleanup_free_ char *fs = NULL;
int r, unified;
unified = cg_all_unified();
unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* Doesn't apply to unified hierarchy */
@@ -1076,7 +1075,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
if (controller && (isempty(path) || path_equal(path, "/")))
return false;
unified = cg_all_unified();
unified = cg_unified(controller);
if (unified < 0)
return unified;
@@ -2224,9 +2223,10 @@ int cg_kernel_controllers(Set *controllers) {
return 0;
}
static thread_local int unified_cache = -1;
static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
static int cg_update_unified(void) {
int cg_all_unified(void) {
struct statfs fs;
/* Checks if we support the unified hierarchy. Returns an
@@ -2234,24 +2234,47 @@ int cg_all_unified(void) {
* have any other trouble determining if the unified hierarchy
* is supported. */
if (unified_cache >= 0)
return unified_cache;
if (unified_cache >= CGROUP_UNIFIED_NONE)
return 0;
if (statfs("/sys/fs/cgroup/", &fs) < 0)
return -errno;
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
unified_cache = true;
else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
unified_cache = false;
else
unified_cache = CGROUP_UNIFIED_ALL;
else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
return -errno;
unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ?
CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE;
} else
return -ENOMEDIUM;
return unified_cache;
return 0;
}
int cg_unified(const char *controller) {
int r;
r = cg_update_unified();
if (r < 0)
return r;
if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER))
return unified_cache >= CGROUP_UNIFIED_SYSTEMD;
else
return unified_cache >= CGROUP_UNIFIED_ALL;
}
int cg_all_unified(void) {
return cg_unified(NULL);
}
void cg_unified_flush(void) {
unified_cache = -1;
unified_cache = CGROUP_UNIFIED_UNKNOWN;
}
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
@@ -2333,6 +2356,50 @@ bool cg_is_legacy_wanted(void) {
return !cg_is_unified_wanted();
}
bool cg_is_unified_systemd_controller_wanted(void) {
static thread_local int wanted = -1;
int r, unified;
/* If the unified hierarchy is requested in full, no need to
* bother with this. */
if (cg_is_unified_wanted())
return 0;
/* If the hierarchy is already mounted, then follow whatever
* was chosen for it. */
unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
if (unified >= 0)
return unified;
/* Otherwise, let's see what the kernel command line has to
* say. Since checking that is expensive, let's cache the
* result. */
if (wanted >= 0)
return wanted;
r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
if (r > 0) {
wanted = false;
} else {
_cleanup_free_ char *value = NULL;
r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
if (r < 0)
return true;
if (r == 0)
wanted = true;
else
wanted = parse_boolean(value) <= 0;
}
return wanted;
}
bool cg_is_legacy_systemd_controller_wanted(void) {
return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted();
}
int cg_weight_parse(const char *s, uint64_t *ret) {
uint64_t u;
int r;
+10
View File
@@ -112,6 +112,13 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) {
(x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX);
}
typedef enum CGroupUnified {
CGROUP_UNIFIED_UNKNOWN = -1,
CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */
CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */
CGROUP_UNIFIED_ALL = 2, /* Both systemd and controllers on unified */
} CGroupUnified;
/*
* General rules:
*
@@ -226,10 +233,13 @@ int cg_kernel_controllers(Set *controllers);
bool cg_ns_supported(void);
int cg_all_unified(void);
int cg_unified(const char *controller);
void cg_unified_flush(void);
bool cg_is_unified_wanted(void);
bool cg_is_legacy_wanted(void);
bool cg_is_unified_systemd_controller_wanted(void);
bool cg_is_legacy_systemd_controller_wanted(void);
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;