X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fcgroup.c;h=f0420ebc0944a5f60b64f9e851b142dfcaf82d3c;hb=e954c9cfa687065244adebaa15314082ba6d7ec2;hp=6a6c5049b39df1377b571baa2d9e51e0fe80c8b4;hpb=f3669545238702f8ffee7b743ca4347b785b558a;p=elogind.git diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 6a6c5049b..f0420ebc0 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -335,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); @@ -345,54 +345,127 @@ 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 int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { - char *path = NULL; + _cleanup_free_ char *path; + bool was_in_hash = false; int r; - bool is_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,23 +478,38 @@ 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; - u->cgroup_mask = mask; + u->cgroup_realized_mask = mask; return 0; } +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); @@ -430,19 +518,27 @@ 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) + 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 +553,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 +583,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); } @@ -497,7 +608,6 @@ static void unit_queue_siblings(Unit *u) { int unit_realize_cgroup(Unit *u) { CGroupContext *c; - int r; assert(u); @@ -519,14 +629,8 @@ int unit_realize_cgroup(Unit *u) { /* Add all sibling slices to the cgroup queue. */ unit_queue_siblings(u); - /* And realize this one now */ - 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; + /* And realize this one now (and apply the values) */ + return unit_realize_cgroup_now(u); } void unit_destroy_cgroup(Unit *u) { @@ -546,7 +650,7 @@ void unit_destroy_cgroup(Unit *u) { free(u->cgroup_path); u->cgroup_path = NULL; u->cgroup_realized = false; - u->cgroup_mask = 0; + u->cgroup_realized_mask = 0; }