pid1: stop refusing to boot with cgroup v1
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 2 Nov 2024 16:07:22 +0000 (17:07 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 6 Nov 2024 13:43:25 +0000 (13:43 +0000)
Since v256 we completely fail to boot if v1 is configured. Fedora 41 was just
released with v256.7 and this is probably the first major exposure of users to
this code. It turns out not work very well. Fedora switched to v2 as default in
F31 (2019) and at that time some people added configuration to use v1 either
because of Docker or for other reasons. But it's been long enough ago that
people don't remember this and are now very unhappy when the system refuses to
boot after an upgrade.

Refusing to boot is also unnecessarilly punishing to users. For machines that
are used remotely, this could mean somebody needs to physically access the
machine. For other users, the machine might be the only way to access the net
and help, and people might not know how to set kernel parameters without some
docs. And because this is in systemd, after an upgrade all boot choices are
affected, and it's not possible to e.g. select an older kernel for boot. And
crashing the machine doesn't really serve our goal either: we were giving a
hint how to continue using v1 and nothing else.

If the new override is configured, warn and immediately boot to v1.
If v1 is configured w/o the override, warn and wait 30 s and boot to v2.
Also give a hint how to switch to v2.

https://bugzilla.redhat.com/show_bug.cgi?id=2323323
https://bugzilla.redhat.com/show_bug.cgi?id=2323345
https://bugzilla.redhat.com/show_bug.cgi?id=2322467
https://www.reddit.com/r/Fedora/comments/1gfcyw9/refusing_to_run_under_cgroup_01_sy_specified_on/

The advice is to set systemd.unified_cgroup_hierarchy=1 (instead of removing
systemd.unified_cgroup_hierarchy=0). I think this is easier to convey. Users
who are understand what is going on can just remove the option instead.

The caching is dropped in cg_is_legacy_wanted(). It turns out that the
order in which those functions are called during early setup is very fragile.
If cg_is_legacy_wanted() is called before we have set up the v2 hierarchy,
we incorrectly cache a true answer. The function is called just a handful
of times at most, so we don't really need to cache the response.

NEWS
src/core/main.c
src/shared/cgroup-setup.c
src/shared/cgroup-setup.h
src/shared/mount-setup.c

diff --git a/NEWS b/NEWS
index b7baf6050abc077ceb6a3b9ddd1499d9728c1e15..abd7d887c873b4a9c08d302cb58d868de7b149a1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -51,6 +51,12 @@ CHANGES WITH 257 in spe:
           too many systems, because most NVMe devices only know a namespace 1
           by default.
 
+        * Support for cgroup v1 ('legacy' and 'hybrid' hierarchies) is now
+          considered obsolete and systemd by default will ignore configuration
+          that enables them. To forcibly reenable cgroup v1 support,
+          SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 must additionally be set on the
+          kernel command line.
+
        Announcements of Future Feature Removals:
 
         * The D-Bus method org.freedesktop.systemd1.StartAuxiliaryScope() is
@@ -64,11 +70,8 @@ CHANGES WITH 257 in spe:
           will be phased out in a future release in 2025, i.e. we expect to bump
           the minimum baseline to v5.4 then too.
 
-        * Support for cgroup v1 ('legacy' and 'hybrid' hierarchies) is now
-          considered obsolete and systemd by default will refuse to boot under
-          it. To forcibly reenable cgroup v1 support,
-          SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 must be set on kernel command
-          line. The complete removal of cgroup v1 is scheduled for v258.
+        * The complete removal of support for cgroup v1 ('legacy' and 'hybrid'
+          hierarchies) is scheduled for v258.
 
         * Support for System V service scripts is deprecated and will be
           removed in v258. Please make sure to update your software
index 93a1e221f749e1533f5eb08a869b32001b273f95..38216fa7a665f6434c24d8ee5c8ffc1f4b12c3b6 100644 (file)
@@ -3170,21 +3170,11 @@ int main(int argc, char *argv[]) {
                 }
 
                 if (!skip_setup) {
-                        /* Before we actually start deleting cgroup v1 code, make it harder to boot
-                         * in cgroupv1 mode first. See also #30852. */
-
                         r = mount_cgroup_legacy_controllers(loaded_policy);
                         if (r < 0) {
-                                if (r == -ERFKILL)
-                                        error_message = "Refusing to run under cgroup v1, SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 not specified on kernel command line";
-                                else
-                                        error_message = "Failed to mount cgroup v1 hierarchy";
+                                error_message = "Failed to mount cgroup v1 hierarchy";
                                 goto finish;
                         }
-                        if (r > 0) {
-                                log_full(LOG_CRIT, "Legacy cgroup v1 support selected. This is no longer supported. Will proceed anyway after 30s.");
-                                (void) usleep_safe(30 * USEC_PER_SEC);
-                        }
                 }
 
                 /* The efivarfs is now mounted, let's lock down the system token. */
index d415d13d41e6291e453e29a10dda1f8616e44ca9..49d40f60d855ffc9a3dbc80a2538b65e285ad43b 100644 (file)
@@ -92,11 +92,14 @@ bool cg_is_unified_wanted(void) {
         if (r >= 0)
                 return (wanted = r >= CGROUP_UNIFIED_ALL);
 
-        /* If we were explicitly passed systemd.unified_cgroup_hierarchy, respect that. */
+        /* 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)
-                return (wanted = 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. */
@@ -106,23 +109,21 @@ bool cg_is_unified_wanted(void) {
                 return (wanted = true);
 
         /* If any controller is in use as v1, don't use unified. */
-        return (wanted = (cg_any_controller_used_for_v1() <= 0));
-}
+        if (cg_any_controller_used_for_v1() > 0)
+                return (wanted = false);
 
-bool cg_is_legacy_wanted(void) {
-        static thread_local int wanted = -1;
+        return (wanted = true);
 
-        /* If we have a cached value, return that. */
-        if (wanted >= 0)
-                return wanted;
+}
 
+bool cg_is_legacy_wanted(void) {
         /* Check if we have cgroup v2 already mounted. */
         if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL)
-                return (wanted = false);
+                return false;
 
         /* Otherwise, assume that at least partial legacy is wanted,
          * since cgroup v2 should already be mounted at this point. */
-        return (wanted = true);
+        return true;
 }
 
 bool cg_is_hybrid_wanted(void) {
@@ -151,20 +152,28 @@ bool cg_is_hybrid_wanted(void) {
         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) {
-        bool force;
+        int r;
+        bool b;
 
-        if (!cg_is_legacy_wanted())
-                return false;
+        /* Require both systemd.unified_cgroup_hierarchy=0 and SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1. */
 
-        /* If in container, we have to follow host's cgroup hierarchy. */
-        if (detect_container() > 0)
-                return true;
+        if (!cg_is_legacy_enabled())
+                return false;
 
-        if (proc_cmdline_get_bool("SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE", /* flags = */ 0, &force) < 0)
+        r = proc_cmdline_get_bool("SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE", /* flags = */ 0, &b);
+        if (r <= 0 || !b)
                 return false;
 
-        return force;
+        return true;
 }
 
 int cg_weight_parse(const char *s, uint64_t *ret) {
index b8473097cf5808dd0a1f5852002eac79709fc8d9..31c4ea1aced2c219a576625f147a540ad31445cb 100644 (file)
@@ -10,6 +10,7 @@
 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);
index ba291bd76f07345bc52d4e34c5357232f4fb8993..d5009fb59ed158da8017f197e817c693c8f85266 100644 (file)
@@ -512,12 +512,28 @@ 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;
 
-        if (!cg_is_legacy_force_enabled())
-                return -ERFKILL;
-
         FOREACH_ELEMENT(mp, cgroupv1_mount_table) {
                 r = mount_one(mp, loaded_policy);
                 if (r < 0)