X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fcgroup.c;h=707ce470a123ba8a4d7f0a08e85ae93e69b18765;hb=d4fdc205a4610965cee46408dbd046c922e7620c;hp=79467a82cea8ea4ac0c3a659017488067dded84f;hpb=4ad490007b70e6ac18d3cb04fa2ed92eba1451fa;p=elogind.git diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 79467a82c..707ce470a 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -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) @@ -339,7 +335,7 @@ CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) { return mask; } -static CGroupControllerMask unit_get_cgroup_mask(Unit *u) { +CGroupControllerMask unit_get_cgroup_mask(Unit *u) { CGroupContext *c; c = unit_get_cgroup_context(u); @@ -349,90 +345,210 @@ static CGroupControllerMask unit_get_cgroup_mask(Unit *u) { return cgroup_context_get_mask(c); } -static CGroupControllerMask unit_get_members_mask(Unit *u) { - CGroupControllerMask mask = 0; - Unit *m; - Iterator i; - +CGroupControllerMask unit_get_members_mask(Unit *u) { assert(u); - SET_FOREACH(m, u->dependencies[UNIT_BEFORE], i) { + if (u->cgroup_members_mask_valid) + return u->cgroup_members_mask; - if (UNIT_DEREF(m->slice) != u) - continue; + u->cgroup_members_mask = 0; + + if (u->type == UNIT_SLICE) { + Unit *member; + Iterator i; - mask |= unit_get_cgroup_mask(m) | unit_get_members_mask(m); + SET_FOREACH(member, u->dependencies[UNIT_BEFORE], i) { + + if (member == u) + continue; + + if (UNIT_DEREF(member->slice) != u) + continue; + + u->cgroup_members_mask |= + unit_get_cgroup_mask(member) | + unit_get_members_mask(member); + } } - return mask; + u->cgroup_members_mask_valid = true; + return u->cgroup_members_mask; } -static CGroupControllerMask unit_get_siblings_mask(Unit *u) { +CGroupControllerMask unit_get_siblings_mask(Unit *u) { + CGroupControllerMask m; + assert(u); - if (!UNIT_ISSET(u->slice)) - return 0; + if (UNIT_ISSET(u->slice)) + m = unit_get_members_mask(UNIT_DEREF(u->slice)); + else + m = unit_get_cgroup_mask(u) | unit_get_members_mask(u); /* Sibling propagation is only relevant for weight-based * controllers, so let's mask out everything else */ - return unit_get_members_mask(UNIT_DEREF(u->slice)) & - (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT); + return m & (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT); +} + +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_cgroup_members_masks(Unit *u) { + CGroupControllerMask m; + bool more; + + assert(u); + + /* Calculate subtree mask */ + m = unit_get_cgroup_mask(u) | unit_get_members_mask(u); + + /* See if anything changed from the previous invocation. If + * not, we're done. */ + if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask) + return; + + more = + u->cgroup_subtree_mask_valid && + ((m & ~u->cgroup_subtree_mask) != 0) && + ((~m & u->cgroup_subtree_mask) == 0); + + u->cgroup_subtree_mask = m; + u->cgroup_subtree_mask_valid = true; + + if (UNIT_ISSET(u->slice)) { + Unit *s = UNIT_DEREF(u->slice); + + if (more) + /* There's more set now than before. We + * propagate the new mask to the parent's mask + * (not caring if it actually was valid or + * not). */ + + s->cgroup_members_mask |= m; + + else + /* There's less set now than before (or we + * don't know), we need to recalculate + * everything, so let's invalidate the + * parent's members mask */ + + s->cgroup_members_mask_valid = false; + + /* And now make sure that this change also hits our + * grandparents */ + unit_update_cgroup_members_masks(s); + } +} + +static const char *migrate_callback(CGroupControllerMask mask, void *userdata) { + Unit *u = userdata; + + assert(mask != 0); + assert(u); + + while (u) { + if (u->cgroup_path && + u->cgroup_realized && + (u->cgroup_realized_mask & mask) == mask) + return u->cgroup_path; + + u = UNIT_DEREF(u->slice); + } + + return NULL; } static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { - char *path = NULL; + _cleanup_free_ char *path = NULL; int r; assert(u); path = unit_default_cgroup_path(u); if (!path) - return -ENOMEM; + return log_oom(); - /* First, create our own group */ - r = cg_create_with_mask(mask, path); - if (r < 0) - log_error("Failed to create cgroup %s: %s", path, strerror(-r)); + r = hashmap_put(u->manager->cgroup_unit, path, u); + if (r < 0) { + log_error(r == -EEXIST ? "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s", path, strerror(-r)); + return r; + } + if (r > 0) { + u->cgroup_path = path; + path = NULL; + } - /* 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 (r < 0) - log_error("Failed to migrate cgroup %s: %s", path, strerror(-r)); + /* First, create our own group */ + r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path); + if (r < 0) { + log_error("Failed to create cgroup %s: %s", u->cgroup_path, strerror(-r)); + return r; } - /* And remember the new data */ - free(u->cgroup_path); - u->cgroup_path = path; + /* Keep track that this is now realized */ u->cgroup_realized = true; - u->cgroup_mask = mask; + u->cgroup_realized_mask = mask; + + /* Then, possibly move things over */ + r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u); + if (r < 0) + log_warning("Failed to migrate cgroup from to %s: %s", u->cgroup_path, strerror(-r)); return 0; } -static void unit_realize_cgroup_now(Unit *u) { +static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) { + assert(u); + + return u->cgroup_realized && u->cgroup_realized_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); 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; } - 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) - return; + 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 */ - 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) { @@ -440,19 +556,22 @@ 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; } unsigned manager_dispatch_cgroup_queue(Manager *m) { Unit *i; unsigned n = 0; + int r; 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); + 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++; } @@ -474,9 +593,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); } @@ -484,20 +616,20 @@ static void unit_queue_siblings(Unit *u) { } } -void unit_realize_cgroup(Unit *u) { +int unit_realize_cgroup(Unit *u) { CGroupContext *c; 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 @@ -507,11 +639,8 @@ void unit_realize_cgroup(Unit *u) { /* Add all sibling slices to the cgroup queue. */ unit_queue_siblings(u); - /* And realize this one now */ - unit_realize_cgroup_now(u); - - /* And apply the values */ - cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path); + /* And realize this one now (and apply the values) */ + return unit_realize_cgroup_now(u); } void unit_destroy_cgroup(Unit *u) { @@ -522,14 +651,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; + u->cgroup_realized_mask = 0; + } pid_t unit_search_main_pid(Unit *u) { @@ -571,8 +703,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 +724,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 +761,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 +781,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 +854,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; }