cgroup v1 preliminaries (#36622)

This commit is contained in:
Mike Yuan
2025-03-16 20:27:48 +01:00
committed by GitHub
17 changed files with 90 additions and 534 deletions
+13 -3
View File
@@ -4,7 +4,6 @@
set -eux
set -o pipefail
# default to Debian testing
DISTRO="${DISTRO:-debian}"
RELEASE="${RELEASE:-bookworm}"
SALSA_URL="${SALSA_URL:-https://salsa.debian.org/systemd-team/systemd.git}"
@@ -24,9 +23,19 @@ create_container() {
sudo lxc-create -n "$CONTAINER" -t download -- -d "$DISTRO" -r "$RELEASE" -a "$ARCH"
# unconfine the container, otherwise some tests fail
echo 'lxc.apparmor.profile = unconfined' | sudo tee -a "/var/lib/lxc/$CONTAINER/config"
#
# disable automatic cgroup setup, instead let pid1 figure it out in mount_setup().
# This is especially important to ensure we get unified cgroup hierarchy
#
# FIXME: remove cgroup workarounds once the host runs on unified hierarchy
sudo tee "/var/lib/lxc/$CONTAINER/config.systemd_upstream" <<EOF
lxc.apparmor.profile = unconfined
lxc.mount.auto =
lxc.mount.auto = proc:mixed sys:mixed
lxc.init.cmd = /sbin/init systemd.unified_cgroup_hierarchy=1
EOF
sudo lxc-start -n "$CONTAINER"
sudo lxc-start -n "$CONTAINER" --define "lxc.include=/var/lib/lxc/$CONTAINER/config.systemd_upstream"
# enable source repositories so that apt-get build-dep works
sudo lxc-attach -n "$CONTAINER" -- sh -ex <<EOF
@@ -112,6 +121,7 @@ EOF
../systemd_*.dsc \
-o "$ARTIFACTS_DIR" \
-- lxc -s "$CONTAINER" \
--define "lxc.include=/var/lib/lxc/$CONTAINER/config.systemd_upstream" \
|| [ $? -eq 2 ]
;;
*)
+2 -1
View File
@@ -52,7 +52,8 @@ REQUIREMENTS:
# FIXME: actually drop compat glue before v258
Linux kernel ≥ 5.6 for getrandom() GRND_INSECURE
≥ 5.7 for CLONE_INTO_CGROUP, BPF links and the BPF LSM hook
≥ 5.7 for CLONE_INTO_CGROUP, cgroup2fs memory_recursiveprot option,
BPF links and the BPF LSM hook
⚠️ Kernel versions below 5.7 ("recommended baseline") have significant gaps
in functionality and are not recommended for use with this version
+4 -10
View File
@@ -263,16 +263,10 @@
returns the user slice (as managed by the user's systemd instance)
of a process.</para>
<para><function>sd_pid_get_cgroup()</function> returns the control
group path of the specified process, relative to the root of the
hierarchy. Returns the path without trailing slash, except for
processes located in the root control group, where "/" is
returned. To find the actual control group path in the file system,
the returned path needs to be prefixed with
<filename>/sys/fs/cgroup/</filename> (if the unified control group
setup is used), or
<filename>/sys/fs/cgroup/<replaceable>HIERARCHY</replaceable>/</filename>
(if the legacy multi-hierarchy control group setup is used).</para>
<para><function>sd_pid_get_cgroup()</function> returns the control group path of the specified process,
relative to the root of the hierarchy. Returns the path without trailing slash, except for processes
located in the root control group, where "/" is returned. To find the actual control group path
in the file system, the returned path needs to be prefixed with <filename>/sys/fs/cgroup/</filename>.</para>
<para>If the <varname>pid</varname> parameter of any of these
functions is passed as 0, the operation is executed for the
-14
View File
@@ -2316,20 +2316,6 @@ static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] =
DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
bool is_cgroup_fs(const struct statfs *s) {
return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
is_fs_type(s, CGROUP2_SUPER_MAGIC);
}
bool fd_is_cgroup_fs(int fd) {
struct statfs s;
if (fstatfs(fd, &s) < 0)
return -errno;
return is_cgroup_fs(&s);
}
static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_CPU] = "cpu",
[CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
-3
View File
@@ -329,9 +329,6 @@ static inline int cg_unified(void) {
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
bool is_cgroup_fs(const struct statfs *s);
bool fd_is_cgroup_fs(int fd);
typedef enum ManagedOOMMode {
MANAGED_OOM_AUTO,
MANAGED_OOM_KILL,
+17 -8
View File
@@ -3201,14 +3201,6 @@ int main(int argc, char *argv[]) {
goto finish;
}
if (!skip_setup) {
r = mount_cgroup_legacy_controllers(loaded_policy);
if (r < 0) {
error_message = "Failed to mount cgroup v1 hierarchy";
goto finish;
}
}
/* The efivarfs is now mounted, let's lock down the system token. */
lock_down_efi_variables();
} else {
@@ -3310,6 +3302,23 @@ int main(int argc, char *argv[]) {
log_execution_mode(&first_boot);
r = cg_has_legacy();
if (r < 0) {
error_message = "Failed to check cgroup hierarchy";
goto finish;
}
if (r > 0) {
r = log_full_errno(LOG_EMERG, SYNTHETIC_ERRNO(EPROTO),
"Detected cgroup v1 hierarchy at /sys/fs/cgroup/, which is no longer supported by current version of systemd.\n"
"Please instruct your initrd to mount cgroup v2 (unified) hierarchy,\n"
"possibly by removing any stale kernel command line options, such as:\n"
" systemd.legacy_systemd_cgroup_controller=1\n"
" systemd.unified_cgroup_hierarchy=0");
error_message = "Detected unsupported legacy cgroup hierarchy, refusing execution";
goto finish;
}
r = initialize_runtime(skip_setup,
first_boot,
&saved_rlimit_nofile,
+3 -20
View File
@@ -26,6 +26,7 @@
#include "loopback-setup.h"
#include "missing_syscall.h"
#include "mkdir-label.h"
#include "mount-setup.h"
#include "mount-util.h"
#include "mountpoint-util.h"
#include "namespace-util.h"
@@ -1373,34 +1374,16 @@ static int mount_private_sysfs(const MountEntry *m, const NamespaceParameters *p
return mount_private_apivfs("sysfs", mount_entry_path(m), "/sys", /* opts = */ NULL, p->runtime_scope);
}
static bool check_recursiveprot_supported(void) {
int r;
/* memory_recursiveprot is only supported for kernels >= 5.7. Note mount_option_supported uses fsopen()
* and fsconfig() which are supported for kernels >= 5.2. So if mount_option_supported() returns an
* error, we can assume memory_recursiveprot is not supported. */
r = mount_option_supported("cgroup2", "memory_recursiveprot", NULL);
if (r < 0)
log_debug_errno(r, "Failed to determine whether the 'memory_recursiveprot' mount option is supported, assuming not: %m");
else if (r == 0)
log_debug("This kernel version does not support 'memory_recursiveprot', not using mount option.");
return r > 0;
}
static int mount_private_cgroup2fs(const MountEntry *m, const NamespaceParameters *p) {
_cleanup_free_ char *opts = NULL;
assert(m);
assert(p);
if (check_recursiveprot_supported()) {
opts = strdup(strempty(mount_entry_options(m)));
if (cgroupfs_recursiveprot_supported()) {
opts = strextend_with_separator(NULL, ",", mount_entry_options(m) ?: POINTER_MAX, "memory_recursiveprot");
if (!opts)
return -ENOMEM;
if (!strextend_with_separator(&opts, ",", "memory_recursiveprot"))
return -ENOMEM;
}
return mount_private_apivfs("cgroup2", mount_entry_path(m), "/sys/fs/cgroup", opts ?: mount_entry_options(m), p->runtime_scope);
-3
View File
@@ -52,9 +52,6 @@ char** taint_strv(void) {
if (readlink_malloc("/var/run", &var_run) < 0 || !PATH_IN_SET(var_run, "../run", "/run"))
stage[n++] = "var-run-bad";
if (cg_all_unified() == 0)
stage[n++] = "cgroupsv1";
if (clock_is_localtime(NULL) > 0)
stage[n++] = "local-hwclock";
+27 -158
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <threads.h>
#include <unistd.h>
#include "cgroup-setup.h"
@@ -9,171 +8,15 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "missing_magic.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "recurse-dir.h"
#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;
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);
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;
/* Ignore controllers we don't care about. */
if (cgroup_controller_from_string(name) < 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;
int r;
/* If we have a cached value, return that. */
if (wanted >= 0)
return wanted;
/* If the hierarchy is already mounted, then follow whatever was chosen for it. */
r = cg_unified_cached(true);
if (r >= 0)
return (wanted = r >= CGROUP_UNIFIED_ALL);
/* If we have explicit configuration for v1 or v2, respect that. */
if (cg_is_legacy_force_enabled())
return (wanted = false);
bool b;
r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", /* flags = */ 0, &b);
if (r > 0 && b)
return (wanted = true);
/* If we passed cgroup_no_v1=all with no other instructions, it seems highly unlikely that we want to
* use hybrid or legacy hierarchy. */
_cleanup_free_ char *c = NULL;
r = proc_cmdline_get_key("cgroup_no_v1", 0, &c);
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 = true);
}
bool cg_is_legacy_wanted(void) {
/* Check if we have cgroup v2 already mounted. */
if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL)
return false;
/* Otherwise, assume that at least partial legacy is wanted,
* since cgroup v2 should already be mounted at this point. */
return true;
}
bool cg_is_hybrid_wanted(void) {
static thread_local int wanted = -1;
int r;
/* If we have a cached value, return that. */
if (wanted >= 0)
return wanted;
/* If the hierarchy is already mounted, then follow whatever was chosen for it. */
if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL)
return (wanted = false);
/* Otherwise, let's see what the kernel command line has to say. Since checking is expensive, cache
* a non-error result.
* The meaning of the kernel option is reversed wrt. to the return value of this function, hence the
* negation. */
bool b;
r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", /* flags = */ 0, &b);
if (r > 0)
return (wanted = !b);
/* The default hierarchy is "unified". But if this is reached, it means that unified hierarchy was
* not mounted, so return true too. */
return (wanted = true);
}
bool cg_is_legacy_enabled(void) {
int r;
bool b;
r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", /* flags = */ 0, &b);
return r > 0 && !b;
}
bool cg_is_legacy_force_enabled(void) {
int r;
bool b;
/* Require both systemd.unified_cgroup_hierarchy=0 and SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1. */
if (!cg_is_legacy_enabled())
return false;
r = proc_cmdline_get_bool("SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE", /* flags = */ 0, &b);
if (r <= 0 || !b)
return false;
return true;
}
int cg_weight_parse(const char *s, uint64_t *ret) {
uint64_t u;
@@ -1109,3 +952,29 @@ int cg_uninstall_release_agent(const char *controller) {
return 0;
}
int cg_has_legacy(void) {
struct statfs fs;
/* Checks if any legacy controller/hierarchy is mounted. */
if (statfs("/sys/fs/cgroup/", &fs) < 0) {
if (errno == ENOENT) /* sysfs not mounted? */
return false;
return log_error_errno(errno, "Failed to statfs /sys/fs/cgroup/: %m");
}
if (is_fs_type(&fs, CGROUP2_SUPER_MAGIC) ||
is_fs_type(&fs, SYSFS_MAGIC)) /* not mounted yet */
return false;
if (is_fs_type(&fs, TMPFS_MAGIC)) {
log_info("Found tmpfs on /sys/fs/cgroup/, assuming legacy hierarchy.");
return true;
}
return log_error_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
"Unknown filesystem type %llx mounted on /sys/fs/cgroup/.",
(unsigned long long) fs.f_type);
}
+2 -6
View File
@@ -7,12 +7,6 @@
#include "cgroup-util.h"
bool cg_is_unified_wanted(void);
bool cg_is_legacy_wanted(void);
bool cg_is_hybrid_wanted(void);
bool cg_is_legacy_enabled(void);
bool cg_is_legacy_force_enabled(void);
int cg_weight_parse(const char *s, uint64_t *ret);
int cg_cpu_weight_parse(const char *s, uint64_t *ret);
int cg_cpu_shares_parse(const char *s, uint64_t *ret);
@@ -46,3 +40,5 @@ int cg_trim_v1_controllers(CGroupMask supported, CGroupMask mask, const char *pa
int cg_install_release_agent(const char *controller, const char *agent);
int cg_uninstall_release_agent(const char *controller);
int cg_has_legacy(void);
+17 -210
View File
@@ -52,27 +52,16 @@ typedef struct MountPoint {
MountMode mode;
} MountPoint;
/* The first three entries we might need before SELinux is up. The
* fourth (securityfs) is needed by IMA to load a custom policy. The
* other ones we can delay until SELinux and IMA are loaded. When
* SMACK is enabled we need smackfs, too, so it's a fifth one. */
#if ENABLE_SMACK
#define N_EARLY_MOUNT 5
#else
#define N_EARLY_MOUNT 4
#endif
static bool check_recursiveprot_supported(void) {
bool cgroupfs_recursiveprot_supported(void) {
int r;
if (!cg_is_unified_wanted())
return false;
/* Added in kernel 5.7 */
r = mount_option_supported("cgroup2", "memory_recursiveprot", NULL);
r = mount_option_supported("cgroup2", "memory_recursiveprot", /* value = */ NULL);
if (r < 0)
log_debug_errno(r, "Failed to determine whether the 'memory_recursiveprot' mount option is supported, assuming not: %m");
log_debug_errno(r, "Failed to determine whether cgroupfs supports 'memory_recursiveprot' mount option, assuming not: %m");
else if (r == 0)
log_debug("This kernel version does not support 'memory_recursiveprot', not using mount option.");
log_debug("'memory_recursiveprot' not supported by cgroupfs, not using mount option.");
return r > 0;
}
@@ -103,11 +92,9 @@ static const MountPoint mount_table[] = {
{ "tmpfs", "/run", "tmpfs", "mode=0755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate,memory_recursiveprot", MS_NOSUID|MS_NOEXEC|MS_NODEV,
check_recursiveprot_supported, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
cgroupfs_recursiveprot_supported, MNT_FATAL|MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
{ "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
{ "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
NULL, MNT_FATAL|MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
#if ENABLE_PSTORE
{ "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
@@ -120,6 +107,16 @@ static const MountPoint mount_table[] = {
NULL, MNT_NONE, },
};
/* The first three entries we might need before SELinux is up. The
* fourth (securityfs) is needed by IMA to load a custom policy. The
* other ones we can delay until SELinux and IMA are loaded. When
* SMACK is enabled we need smackfs, too, so it's a fifth one. */
#if ENABLE_SMACK
#define N_EARLY_MOUNT 5
#else
#define N_EARLY_MOUNT 4
#endif
assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
bool mount_point_is_api(const char *path) {
@@ -249,56 +246,6 @@ int mount_setup_early(void) {
return mount_points_setup(N_EARLY_MOUNT, /* loaded_policy= */ false);
}
static const char *join_with(const char *controller) {
static const char* const pairs[] = {
"cpu", "cpuacct",
"net_cls", "net_prio",
NULL
};
assert(controller);
/* This will lookup which controller to mount another controller with. Input is a controller name, and output
* is the other controller name. The function works both ways: you can input one and get the other, and input
* the other to get the one. */
STRV_FOREACH_PAIR(x, y, pairs) {
if (streq(controller, *x))
return *y;
if (streq(controller, *y))
return *x;
}
return NULL;
}
static int symlink_controller(const char *target, const char *alias) {
const char *a;
int r;
assert(target);
assert(alias);
a = strjoina("/sys/fs/cgroup/", alias);
r = symlink_idempotent(target, a, false);
if (r < 0)
return log_error_errno(r, "Failed to create symlink %s: %m", a);
#if HAVE_SMACK_RUN_LABEL
const char *p;
p = strjoina("/sys/fs/cgroup/", target);
r = mac_smack_copy(a, p);
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", p, a);
#endif
return 0;
}
#if HAVE_SELINUX || ENABLE_SMACK
static int relabel_cb(
RecurseDirEvent event,
@@ -486,143 +433,3 @@ int mount_setup(bool loaded_policy, bool leave_propagation) {
return 0;
}
static const MountPoint cgroupv1_mount_table[] = {
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP, MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
{ "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_legacy_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
};
static void relabel_cgroup_legacy_hierarchy(void) {
#if HAVE_SELINUX || ENABLE_SMACK
struct statfs st;
assert(cg_is_legacy_wanted());
/* Temporarily remount the root cgroup filesystem to give it a proper label. Do this
only when the filesystem has been already populated by a previous instance of systemd
running from initrd. Otherwise don't remount anything and leave the filesystem read-write
for the cgroup filesystems to be mounted inside. */
if (statfs("/sys/fs/cgroup", &st) < 0)
return (void) log_error_errno(errno, "Failed to determine mount flags for /sys/fs/cgroup/: %m");
if (st.f_flags & ST_RDONLY)
(void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL);
(void) label_fix("/sys/fs/cgroup", 0);
(void) relabel_tree("/sys/fs/cgroup");
if (st.f_flags & ST_RDONLY)
(void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL);
#endif
}
int mount_cgroup_legacy_controllers(bool loaded_policy) {
_cleanup_set_free_ Set *controllers = NULL;
int r;
/* Before we actually start deleting cgroup v1 code, make it harder to boot in cgroupv1 mode first.
* See also #30852. */
if (detect_container() <= 0) { /* If in container, we have to follow host's cgroup hierarchy. Only
* do the deprecation checks below if we're not in a container. */
if (cg_is_legacy_force_enabled())
log_warning("Legacy support for cgroup v1 enabled via SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1.");
else if (cg_is_legacy_enabled()) {
log_full(LOG_CRIT,
"Legacy cgroup v1 configured. This will stop being supported soon.\n"
"Will proceed with cgroup v2 after 30 s.\n"
"Set systemd.unified_cgroup_hierarchy=1 to switch to cgroup v2 "
"or set SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 to reenable v1 temporarily.");
(void) usleep_safe(30 * USEC_PER_SEC);
return 0;
}
}
if (!cg_is_legacy_wanted())
return 0;
FOREACH_ELEMENT(mp, cgroupv1_mount_table) {
r = mount_one(mp, loaded_policy);
if (r < 0)
return r;
}
if (loaded_policy)
relabel_cgroup_legacy_hierarchy();
/* Mount all available cgroup controllers that are built into the kernel. */
r = cg_kernel_controllers(&controllers);
if (r < 0)
return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
for (;;) {
_cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
const char *other_controller;
MountPoint p = {
.what = "cgroup",
.type = "cgroup",
.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
.mode = MNT_IN_CONTAINER,
};
controller = set_steal_first(controllers);
if (!controller)
break;
/* Check if we shall mount this together with another controller */
other_controller = join_with(controller);
if (other_controller) {
_cleanup_free_ char *c = NULL;
/* Check if the other controller is actually available in the kernel too */
c = set_remove(controllers, other_controller);
if (c) {
/* Join the two controllers into one string, and maintain a stable ordering */
if (strcmp(controller, other_controller) < 0)
options = strjoin(controller, ",", other_controller);
else
options = strjoin(other_controller, ",", controller);
if (!options)
return log_oom();
}
}
/* The simple case, where there's only one controller to mount together */
if (!options)
options = TAKE_PTR(controller);
where = path_join("/sys/fs/cgroup", options);
if (!where)
return log_oom();
p.where = where;
p.options = options;
r = mount_one(&p, true);
if (r < 0)
return r;
/* Create symlinks from the individual controller names, in case we have a joined mount */
if (controller)
(void) symlink_controller(options, controller);
if (other_controller)
(void) symlink_controller(options, other_controller);
}
/* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */
(void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs",
MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
"mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP);
return 1;
}
+1 -1
View File
@@ -9,4 +9,4 @@ bool mount_point_ignore(const char *path);
int mount_setup_early(void);
int mount_setup(bool loaded_policy, bool leave_propagation);
int mount_cgroup_legacy_controllers(bool loaded_policy);
bool cgroupfs_recursiveprot_supported(void);
+2 -2
View File
@@ -8,12 +8,12 @@
#include "alloc-util.h"
#include "btrfs-util.h"
#include "cgroup-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "log.h"
#include "macro.h"
#include "missing_magic.h"
#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
@@ -24,7 +24,7 @@
* after all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it
* to remove those again. */
static bool is_physical_fs(const struct statfs *sfs) {
return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
return !is_temporary_fs(sfs) && !is_fs_type(sfs, CGROUP2_SUPER_MAGIC);
}
static int patch_dirfd_mode(
-1
View File
@@ -55,7 +55,6 @@ simple_tests += files(
'test-build-path.c',
'test-bus-util.c',
'test-calendarspec.c',
'test-cgroup-setup.c',
'test-cgroup-util.c',
'test-cgroup.c',
'test-chase.c',
-71
View File
@@ -1,71 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <unistd.h>
#include "alloc-util.h"
#include "cgroup-setup.h"
#include "errno-util.h"
#include "log.h"
#include "proc-cmdline.h"
#include "string-util.h"
#include "tests.h"
static void test_is_wanted_print_one(bool header) {
_cleanup_free_ char *cmdline = NULL;
log_info("-- %s --", __func__);
ASSERT_OK(proc_cmdline(&cmdline));
log_info("cmdline: %s", cmdline);
if (header)
(void) system("findmnt -n /sys/fs/cgroup");
log_info("is_unified_wanted() → %s", yes_no(cg_is_unified_wanted()));
log_info("is_hybrid_wanted() → %s", yes_no(cg_is_hybrid_wanted()));
log_info("is_legacy_wanted() → %s", yes_no(cg_is_legacy_wanted()));
log_info(" ");
}
TEST(is_wanted_print) {
test_is_wanted_print_one(true);
test_is_wanted_print_one(false); /* run twice to test caching */
}
TEST(is_wanted) {
ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE",
"systemd.unified_cgroup_hierarchy", 1));
test_is_wanted_print_one(false);
ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE",
"systemd.unified_cgroup_hierarchy=0", 1));
test_is_wanted_print_one(false);
ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE",
"systemd.unified_cgroup_hierarchy=0 "
"systemd.legacy_systemd_cgroup_controller", 1));
test_is_wanted_print_one(false);
ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE",
"systemd.unified_cgroup_hierarchy=0 "
"systemd.legacy_systemd_cgroup_controller=0", 1));
test_is_wanted_print_one(false);
/* cgroup_no_v1=all implies unified cgroup hierarchy, unless otherwise
* explicitly specified. */
ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE",
"cgroup_no_v1=all", 1));
test_is_wanted_print_one(false);
ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE",
"cgroup_no_v1=all "
"systemd.unified_cgroup_hierarchy=0", 1));
test_is_wanted_print_one(false);
}
static int intro(void) {
if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
return log_tests_skipped("can't read /proc/cmdline");
return EXIT_SUCCESS;
}
DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);
-22
View File
@@ -375,28 +375,6 @@ TEST(mask_supported, .sd_booted = true) {
yes_no(m & CGROUP_CONTROLLER_TO_MASK(c)));
}
TEST(is_cgroup_fs, .sd_booted = true) {
struct statfs sfs;
assert_se(statfs("/sys/fs/cgroup", &sfs) == 0);
if (is_temporary_fs(&sfs))
assert_se(statfs("/sys/fs/cgroup/systemd", &sfs) == 0);
assert_se(is_cgroup_fs(&sfs));
}
TEST(fd_is_cgroup_fs, .sd_booted = true) {
int fd;
fd = open("/sys/fs/cgroup", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
assert_se(fd >= 0);
if (fd_is_temporary_fs(fd)) {
fd = safe_close(fd);
fd = open("/sys/fs/cgroup/systemd", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
assert_se(fd >= 0);
}
assert_se(fd_is_cgroup_fs(fd));
fd = safe_close(fd);
}
TEST(cg_tests) {
int all, hybrid, systemd, r;
+2 -1
View File
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "clock-util.h"
#include "taint.h"
#include "tests.h"
@@ -8,7 +9,7 @@ TEST(taint_string) {
assert_se(a);
log_debug("taint string: '%s'", a);
assert_se(!!strstr(a, "cgroupsv1") == (cg_all_unified() == 0));
assert_se(!!strstr(a, "local-hwclock") == (clock_is_localtime(NULL) > 0));
}
DEFINE_TEST_MAIN(LOG_DEBUG);