chiark / gitweb /
use #pragma once instead of foo*foo define guards
[elogind.git] / src / core / cgroup.c
index 79467a82cea8ea4ac0c3a659017488067dded84f..1a8fd3728f06acf9179a2170d175507b77e8a91e 100644 (file)
@@ -33,7 +33,7 @@ void cgroup_context_init(CGroupContext *c) {
          * structure is preinitialized to 0 */
 
         c->cpu_shares = 1024;
-        c->memory_limit = c->memory_soft_limit = (uint64_t) -1;
+        c->memory_limit = (uint64_t) -1;
         c->blockio_weight = 1000;
 }
 
@@ -41,7 +41,7 @@ void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
         assert(c);
         assert(a);
 
-        LIST_REMOVE(CGroupDeviceAllow, device_allow, c->device_allow, a);
+        LIST_REMOVE(device_allow, c->device_allow, a);
         free(a->path);
         free(a);
 }
@@ -50,7 +50,7 @@ void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODe
         assert(c);
         assert(w);
 
-        LIST_REMOVE(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
+        LIST_REMOVE(device_weights, c->blockio_device_weights, w);
         free(w->path);
         free(w);
 }
@@ -59,7 +59,7 @@ void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockI
         assert(c);
         assert(b);
 
-        LIST_REMOVE(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
+        LIST_REMOVE(device_bandwidths, c->blockio_device_bandwidths, b);
         free(b->path);
         free(b);
 }
@@ -92,9 +92,8 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
                 "%sBlockIOAccounting=%s\n"
                 "%sMemoryAccounting=%s\n"
                 "%sCPUShares=%lu\n"
-                "%sBlockIOWeight%lu\n"
+                "%sBlockIOWeight=%lu\n"
                 "%sMemoryLimit=%" PRIu64 "\n"
-                "%sMemorySoftLimit=%" PRIu64 "\n"
                 "%sDevicePolicy=%s\n",
                 prefix, yes_no(c->cpu_accounting),
                 prefix, yes_no(c->blockio_accounting),
@@ -102,7 +101,6 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
                 prefix, c->cpu_shares,
                 prefix, c->blockio_weight,
                 prefix, c->memory_limit,
-                prefix, c->memory_soft_limit,
                 prefix, cgroup_device_policy_to_string(c->device_policy));
 
         LIST_FOREACH(device_allow, a, c->device_allow)
@@ -114,7 +112,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
 
         LIST_FOREACH(device_weights, w, c->blockio_device_weights)
                 fprintf(f,
-                        "%sBlockIOWeight=%s %lu",
+                        "%sBlockIODeviceWeight=%s %lu",
                         prefix,
                         w->path,
                         w->weight);
@@ -256,15 +254,14 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
         }
 
         if (mask & CGROUP_MEMORY) {
-                char buf[DECIMAL_STR_MAX(uint64_t) + 1];
+                if (c->memory_limit != (uint64_t) -1) {
+                        char buf[DECIMAL_STR_MAX(uint64_t) + 1];
 
-                sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
-                r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
-                if (r < 0)
-                        log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
+                        sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
+                        r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
+                } else
+                        r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1");
 
-                sprintf(buf, "%" PRIu64 "\n", c->memory_soft_limit);
-                cg_set_attribute("memory", path, "memory.soft_limit_in_bytes", buf);
                 if (r < 0)
                         log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
         }
@@ -329,8 +326,7 @@ CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
                 mask |= CGROUP_BLKIO;
 
         if (c->memory_accounting ||
-            c->memory_limit != (uint64_t) -1 ||
-            c->memory_soft_limit != (uint64_t) -1)
+            c->memory_limit != (uint64_t) -1)
                 mask |= CGROUP_MEMORY;
 
         if (c->device_allow || c->device_policy != CGROUP_AUTO)
@@ -380,43 +376,59 @@ static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
 }
 
 static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
-        char *path = NULL;
+        _cleanup_free_ char *path;
         int r;
+        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)
+                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;
+        }
 
         /* First, create our own group */
-        r = cg_create_with_mask(mask, path);
+        r = cg_create_everywhere(u->manager->cgroup_supported, mask, path);
         if (r < 0)
                 log_error("Failed to create cgroup %s: %s", path, strerror(-r));
 
         /* Then, possibly move things over */
-        if (u->cgroup_path && !streq(path, u->cgroup_path)) {
-                r = cg_migrate_with_mask(mask, u->cgroup_path, path);
+        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 (!was_in_hash) {
+                /* Remember the new data */
+                free(u->cgroup_path);
+                u->cgroup_path = path;
+                path = NULL;
         }
 
-        /* And remember the new data */
-        free(u->cgroup_path);
-        u->cgroup_path = path;
         u->cgroup_realized = true;
         u->cgroup_mask = mask;
 
         return 0;
 }
 
-static void unit_realize_cgroup_now(Unit *u) {
+static int unit_realize_cgroup_now(Unit *u) {
         CGroupControllerMask mask;
 
         assert(u);
 
         if (u->in_cgroup_queue) {
-                LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u);
+                LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
                 u->in_cgroup_queue = false;
         }
 
@@ -425,14 +437,14 @@ static void unit_realize_cgroup_now(Unit *u) {
 
         if (u->cgroup_realized &&
             u->cgroup_mask == mask)
-                return;
+                return 0;
 
         /* First, realize parents */
         if (UNIT_ISSET(u->slice))
                 unit_realize_cgroup_now(UNIT_DEREF(u->slice));
 
         /* And then do the real work */
-        unit_create_cgroups(u, mask);
+        return unit_create_cgroups(u, mask);
 }
 
 static void unit_add_to_cgroup_queue(Unit *u) {
@@ -440,7 +452,7 @@ static void unit_add_to_cgroup_queue(Unit *u) {
         if (u->in_cgroup_queue)
                 return;
 
-        LIST_PREPEND(Unit, cgroup_queue, u->manager->cgroup_queue, u);
+        LIST_PREPEND(cgroup_queue, u->manager->cgroup_queue, u);
         u->in_cgroup_queue = true;
 }
 
@@ -451,8 +463,9 @@ unsigned manager_dispatch_cgroup_queue(Manager *m) {
         while ((i = m->cgroup_queue)) {
                 assert(i->in_cgroup_queue);
 
-                unit_realize_cgroup_now(i);
-                cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
+                if (unit_realize_cgroup_now(i) >= 0)
+                        cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
+
                 n++;
         }
 
@@ -484,20 +497,21 @@ static void unit_queue_siblings(Unit *u) {
         }
 }
 
-void unit_realize_cgroup(Unit *u) {
+int unit_realize_cgroup(Unit *u) {
         CGroupContext *c;
+        int r;
 
         assert(u);
 
         c = unit_get_cgroup_context(u);
         if (!c)
-                return;
+                return 0;
 
         /* So, here's the deal: when realizing the cgroups for this
          * 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
@@ -508,10 +522,13 @@ void unit_realize_cgroup(Unit *u) {
         unit_queue_siblings(u);
 
         /* And realize this one now */
-        unit_realize_cgroup_now(u);
+        r = unit_realize_cgroup_now(u);
 
         /* And apply the values */
-        cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
+        if (r >= 0)
+                cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
+
+        return r;
 }
 
 void unit_destroy_cgroup(Unit *u) {
@@ -522,14 +539,17 @@ void unit_destroy_cgroup(Unit *u) {
         if (!u->cgroup_path)
                 return;
 
-        r = cg_trim_with_mask(u->cgroup_mask, u->cgroup_path, true);
+        r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
         if (r < 0)
-                log_error("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
+                log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
+
+        hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
 
         free(u->cgroup_path);
         u->cgroup_path = NULL;
         u->cgroup_realized = false;
         u->cgroup_mask = 0;
+
 }
 
 pid_t unit_search_main_pid(Unit *u) {
@@ -571,8 +591,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);
 
@@ -592,9 +612,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;
         }
@@ -625,9 +649,8 @@ int manager_setup_cgroup(Manager *m) {
                         log_debug("Release agent already installed.");
         }
 
-        /* 4. Realize the system slice and put us in there */
-        a = strappenda(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
-        r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 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;
@@ -646,6 +669,9 @@ int manager_setup_cgroup(Manager *m) {
         /* 6. Figure out which controllers are supported */
         m->cgroup_supported = cg_mask_supported();
 
+        /* 7.  Always enable hierarchial support if it exists... */
+        cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
+
         return 0;
 }
 
@@ -716,13 +742,16 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
         assert(m);
         assert(cgroup);
 
-        r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, true);
-        if (r == 0)
-                return 0;
-
         u = manager_get_unit_by_cgroup(m, cgroup);
-        if (u && UNIT_VTABLE(u)->notify_cgroup_empty)
-                UNIT_VTABLE(u)->notify_cgroup_empty(u);
+        if (u) {
+                r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
+                if (r > 0) {
+                        if (UNIT_VTABLE(u)->notify_cgroup_empty)
+                                UNIT_VTABLE(u)->notify_cgroup_empty(u);
+
+                        unit_add_to_gc_queue(u);
+                }
+        }
 
         return 0;
 }