chiark / gitweb /
bus: rework sd_bus_error APIs
[elogind.git] / src / core / cgroup.c
index f0a97e6818263d039237dbdb691263048ff2dd07..c2b1b7d38d2d9cf2b4e6faff71c753f49f607a5a 100644 (file)
@@ -346,21 +346,8 @@ static CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
 }
 
 static CGroupControllerMask unit_get_members_mask(Unit *u) {
-        CGroupControllerMask mask = 0;
-        Unit *m;
-        Iterator i;
-
         assert(u);
-
-        SET_FOREACH(m, u->dependencies[UNIT_BEFORE], i) {
-
-                if (UNIT_DEREF(m->slice) != u)
-                        continue;
-
-                mask |= unit_get_cgroup_mask(m) | unit_get_members_mask(m);
-        }
-
-        return mask;
+        return u->cgroup_members_mask;
 }
 
 static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
@@ -375,24 +362,44 @@ static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
                 (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT);
 }
 
+static CGroupControllerMask unit_get_target_mask(Unit *u) {
+        CGroupControllerMask mask;
+
+        mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
+        mask &= u->manager->cgroup_supported;
+
+        return mask;
+}
+
+/* Recurse from a unit up through its containing slices, propagating
+ * mask bits upward. A unit is also member of itself. */
+void unit_update_member_masks(Unit *u) {
+        u->cgroup_members_mask |= unit_get_cgroup_mask(u);
+        if (UNIT_ISSET(u->slice)) {
+                Unit *s = UNIT_DEREF(u->slice);
+                s->cgroup_members_mask |= u->cgroup_members_mask;
+                unit_update_member_masks(s);
+        }
+}
+
 static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
-        char *path = NULL;
+        _cleanup_free_ char *path;
         int r;
-        bool is_in_hash = false;
+        bool was_in_hash = false;
 
         assert(u);
 
         path = unit_default_cgroup_path(u);
         if (!path)
-                return -ENOMEM;
+                return log_oom();
 
         r = hashmap_put(u->manager->cgroup_unit, path, u);
         if (r == 0)
-                is_in_hash = true;
-
-        if (r < 0) {
-                log_error("cgroup %s exists already: %s", path, strerror(-r));
-                free(path);
+                was_in_hash = true;
+        else if (r < 0) {
+                log_error(r == -EEXIST ?
+                          "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s",
+                          path, strerror(-r));
                 return r;
         }
 
@@ -405,13 +412,15 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
         if (u->cgroup_path) {
                 r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, path);
                 if (r < 0)
-                        log_error("Failed to migrate cgroup %s: %s", path, strerror(-r));
+                        log_error("Failed to migrate cgroup from %s to %s: %s",
+                                  u->cgroup_path, path, strerror(-r));
         }
 
-        if (!is_in_hash) {
-                /* And remember the new data */
+        if (!was_in_hash) {
+                /* Remember the new data */
                 free(u->cgroup_path);
                 u->cgroup_path = path;
+                path = NULL;
         }
 
         u->cgroup_realized = true;
@@ -420,8 +429,19 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
         return 0;
 }
 
+static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
+        return u->cgroup_realized && u->cgroup_mask == mask;
+}
+
+/* Check if necessary controllers and attributes for a unit are in place.
+ *
+ * If so, do nothing.
+ * If not, create paths, move processes over, and set attributes.
+ *
+ * Returns 0 on success and < 0 on failure. */
 static int unit_realize_cgroup_now(Unit *u) {
         CGroupControllerMask mask;
+        int r;
 
         assert(u);
 
@@ -430,19 +450,28 @@ static int unit_realize_cgroup_now(Unit *u) {
                 u->in_cgroup_queue = false;
         }
 
-        mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
-        mask &= u->manager->cgroup_supported;
+        mask = unit_get_target_mask(u);
 
-        if (u->cgroup_realized &&
-            u->cgroup_mask == mask)
+        /* TODO: Consider skipping this check. It may be redundant. */
+        if (unit_has_mask_realized(u, mask))
                 return 0;
 
         /* First, realize parents */
-        if (UNIT_ISSET(u->slice))
-                unit_realize_cgroup_now(UNIT_DEREF(u->slice));
+        if (UNIT_ISSET(u->slice)) {
+                r = unit_realize_cgroup_now(UNIT_DEREF(u->slice));
+                if (r < 0)
+                        return r;
+        }
 
         /* And then do the real work */
-        return unit_create_cgroups(u, mask);
+        r = unit_create_cgroups(u, mask);
+        if (r < 0)
+                return r;
+
+        /* Finally, apply the necessary attributes. */
+        cgroup_context_apply(unit_get_cgroup_context(u), mask, u->cgroup_path);
+
+        return 0;
 }
 
 static void unit_add_to_cgroup_queue(Unit *u) {
@@ -457,12 +486,14 @@ static void unit_add_to_cgroup_queue(Unit *u) {
 unsigned manager_dispatch_cgroup_queue(Manager *m) {
         Unit *i;
         unsigned n = 0;
+        int r;
 
         while ((i = m->cgroup_queue)) {
                 assert(i->in_cgroup_queue);
 
-                if (unit_realize_cgroup_now(i) >= 0)
-                        cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
+                r = unit_realize_cgroup_now(i);
+                if (r < 0)
+                        log_warning("Failed to realize cgroups for queued unit %s: %s", i->id, strerror(-r));
 
                 n++;
         }
@@ -485,9 +516,22 @@ static void unit_queue_siblings(Unit *u) {
                         if (m == u)
                                 continue;
 
+                        /* Skip units that have a dependency on the slice
+                         * but aren't actually in it. */
                         if (UNIT_DEREF(m->slice) != slice)
                                 continue;
 
+                        /* No point in doing cgroup application for units
+                         * without active processes. */
+                        if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m)))
+                                continue;
+
+                        /* If the unit doesn't need any new controllers
+                         * and has current ones realized, it doesn't need
+                         * any changes. */
+                        if (unit_has_mask_realized(m, unit_get_target_mask(m)))
+                                continue;
+
                         unit_add_to_cgroup_queue(m);
                 }
 
@@ -509,7 +553,7 @@ int unit_realize_cgroup(Unit *u) {
          * unit, we need to first create all parents, but there's more
          * actually: for the weight-based controllers we also need to
          * make sure that all our siblings (i.e. units that are in the
-         * same slice as we are) have cgroup too. Otherwise things
+         * same slice as we are) have cgroups, too. Otherwise things
          * would become very uneven as each of their processes would
          * get as much resources as all our group together. This call
          * will synchronously create the parent cgroups, but will
@@ -519,13 +563,9 @@ int unit_realize_cgroup(Unit *u) {
         /* Add all sibling slices to the cgroup queue. */
         unit_queue_siblings(u);
 
-        /* And realize this one now */
+        /* And realize this one now (and apply the values) */
         r = unit_realize_cgroup_now(u);
 
-        /* And apply the values */
-        if (r >= 0)
-                cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
-
         return r;
 }
 
@@ -589,8 +629,8 @@ pid_t unit_search_main_pid(Unit *u) {
 
 int manager_setup_cgroup(Manager *m) {
         _cleanup_free_ char *path = NULL;
+        char *e;
         int r;
-        char *e, *a;
 
         assert(m);
 
@@ -610,9 +650,13 @@ int manager_setup_cgroup(Manager *m) {
                 return r;
         }
 
-        /* Already in /system.slice? If so, let's cut this off again */
+        /* LEGACY: Already in /system.slice? If so, let's cut this
+         * off. This is to support live upgrades from older systemd
+         * versions where PID 1 was moved there. */
         if (m->running_as == SYSTEMD_SYSTEM) {
                 e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
+                if (!e)
+                        e = endswith(m->cgroup_root, "/system");
                 if (e)
                         *e = 0;
         }
@@ -643,12 +687,8 @@ int manager_setup_cgroup(Manager *m) {
                         log_debug("Release agent already installed.");
         }
 
-        /* 4. Realize the system slice and put us in there */
-        if (m->running_as == SYSTEMD_SYSTEM) {
-                a = strappenda(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
-                r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 0);
-        } else
-                r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
+        /* 4. Make sure we are in the root cgroup */
+        r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
         if (r < 0) {
                 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
                 return r;