v232's cgroup hybrid mode mounted v2 on /sys/fs/cgroup/elogind, which
unfortunately broke other tools which expect v1 there. From v233 on, hybrid
mode instead mounts and uses v2 on /sys/fs/cgroup/unified and keeps
/sys/fs/cgroup/elogind on v1 for compatibility with external tools. However,
to keep elogind live upgrades working, v233+ should be able to recognize v232
layout and keep using it.
This patch adds v232 hybrid mode support. If v232 layout is detected,
cg_unified(SYSTEMD_CGRouP_CONTROLLER) keeps returning %true but
cg_hybrid_unified() returns %false. This keeps process management on cgroup v2
but turns off the parallel layout.
if (r < 0 && errno != ENOENT)
return -errno;
if (r < 0 && errno != ENOENT)
return -errno;
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && cg_hybrid_unified()) {
+ r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path);
+ }
+
* just cuts off the name= prefixed used for named
* hierarchies, if it is specified. */
* just cuts off the name= prefixed used for named
* hierarchies, if it is specified. */
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
- controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ if (cg_hybrid_unified())
+ controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
+ else
+ controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
+ }
e = startswith(controller, "name=");
if (e)
e = startswith(controller, "name=");
if (e)
int cg_trim(const char *controller, const char *path, bool delete_root) {
_cleanup_free_ char *fs = NULL;
int cg_trim(const char *controller, const char *path, bool delete_root) {
_cleanup_free_ char *fs = NULL;
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && cg_hybrid_unified()) {
+ q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
+ if (q < 0)
+ log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
+ }
+
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && cg_hybrid_unified()) {
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
+ }
+
xsprintf(c, PID_FMT "\n", pid);
xsprintf(c, PID_FMT "\n", pid);
- return write_string_file(fs, c, 0);
+ r = write_string_file(fs, c, 0);
+ if (r < 0)
+ return r;
+
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && cg_hybrid_unified()) {
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to attach %d to compat systemd cgroup %s: %m", pid, path);
+ }
+
+ return 0;
}
int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
}
int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
- return chmod_and_chown(fs, mode, uid, gid);
+ r = chmod_and_chown(fs, mode, uid, gid);
+ if (r < 0)
+ return r;
+
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && cg_hybrid_unified()) {
+ r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set group access on compat systemd cgroup %s: %m", path);
+ }
+
+ return 0;
}
int cg_set_task_access(
}
int cg_set_task_access(
- if (cg_unified(controller))
- return 0;
+ if (!cg_unified(controller)) {
+ /* Compatibility, Always keep values for "tasks" in sync with
+ * "cgroup.procs" */
+ if (cg_get_path(controller, path, "tasks", &procs) >= 0)
+ (void) chmod_and_chown(procs, mode, uid, gid);
+ }
- /* Compatibility, Always keep values for "tasks" in sync with
- * "cgroup.procs" */
- if (cg_get_path(controller, path, "tasks", &procs) >= 0)
- (void) chmod_and_chown(procs, mode, uid, gid);
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && cg_hybrid_unified()) {
+ r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set task access on compat systemd cgroup %s: %m", path);
+ }
static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
+/* The hybrid mode was initially implemented in v232 and simply mounted
+ * cgroup v2 on /sys/fs/cgroup/systemd. This unfortunately broke other
+ * tools (such as docker) which expected the v1 "name=systemd" hierarchy
+ * on /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs
+ * v2 on /sys/fs/cgroup/unified and maintains "name=systemd" hierarchy
+ * on /sys/fs/cgroup/systemd for compatibility with other tools.
+ *
+ * To keep live upgrade working, we detect and support v232 layout. When
+ * v232 layout is detected, to keep cgroup v2 process management but
+ * disable the compat dual layout, we return %true on
+ * cg_unified(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
+ */
+static thread_local bool unified_systemd_v232;
+
static int cg_update_unified(void) {
struct statfs fs;
static int cg_update_unified(void) {
struct statfs fs;
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
unified_cache = CGROUP_UNIFIED_ALL;
else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
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;
+ if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
+ F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
+ unified_cache = CGROUP_UNIFIED_SYSTEMD;
+ unified_systemd_v232 = false;
+ } else if (statfs("/sys/fs/cgroup/systemd/", &fs) == 0 &&
+ F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
+ unified_cache = CGROUP_UNIFIED_SYSTEMD;
+ unified_systemd_v232 = true;
+ } else {
+ if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
+ return -errno;
+ if (!F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC))
+ return -ENOMEDIUM;
+ unified_cache = CGROUP_UNIFIED_NONE;
+ }
} else
return -ENOMEDIUM;
#else
} else
return -ENOMEDIUM;
#else
}
#if 0 /// UNNEEDED by elogind
}
#if 0 /// UNNEEDED by elogind
+bool cg_hybrid_unified(void) {
+
+ assert(cg_update_unified() >= 0);
+
+ return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
+}
+
int cg_unified_flush(void) {
unified_cache = CGROUP_UNIFIED_UNKNOWN;
int cg_unified_flush(void) {
unified_cache = CGROUP_UNIFIED_UNKNOWN;