X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fcgroup.c;h=707ce470a123ba8a4d7f0a08e85ae93e69b18765;hb=d4fdc205a4610965cee46408dbd046c922e7620c;hp=c2b1b7d38d2d9cf2b4e6faff71c753f49f607a5a;hpb=6414b7c981378a6eef480f6806d7cbfc98ca22a1;p=elogind.git diff --git a/src/core/cgroup.c b/src/core/cgroup.c index c2b1b7d38..707ce470a 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,24 +345,52 @@ static CGroupControllerMask unit_get_cgroup_mask(Unit *u) { return cgroup_context_get_mask(c); } -static CGroupControllerMask unit_get_members_mask(Unit *u) { +CGroupControllerMask unit_get_members_mask(Unit *u) { assert(u); + + if (u->cgroup_members_mask_valid) + return u->cgroup_members_mask; + + u->cgroup_members_mask = 0; + + if (u->type == UNIT_SLICE) { + Unit *member; + Iterator i; + + 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); + } + } + + 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); } -static CGroupControllerMask unit_get_target_mask(Unit *u) { +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); @@ -373,19 +401,74 @@ static CGroupControllerMask unit_get_target_mask(Unit *u) { /* 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); +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); - s->cgroup_members_mask |= u->cgroup_members_mask; - unit_update_member_masks(s); + + 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) { - _cleanup_free_ char *path; + _cleanup_free_ char *path = NULL; int r; - bool was_in_hash = false; assert(u); @@ -394,43 +477,38 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { 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)); + 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_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) { - r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, path); - if (r < 0) - 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); + if (r > 0) { u->cgroup_path = path; path = NULL; } + /* 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; + } + + /* 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 bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) { - return u->cgroup_realized && u->cgroup_mask == mask; + assert(u); + + return u->cgroup_realized && u->cgroup_realized_mask == mask; } /* Check if necessary controllers and attributes for a unit are in place. @@ -452,7 +530,6 @@ static int unit_realize_cgroup_now(Unit *u) { mask = unit_get_target_mask(u); - /* TODO: Consider skipping this check. It may be redundant. */ if (unit_has_mask_realized(u, mask)) return 0; @@ -541,7 +618,6 @@ static void unit_queue_siblings(Unit *u) { int unit_realize_cgroup(Unit *u) { CGroupContext *c; - int r; assert(u); @@ -553,7 +629,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 cgroups, 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 @@ -564,9 +640,7 @@ int unit_realize_cgroup(Unit *u) { unit_queue_siblings(u); /* And realize this one now (and apply the values) */ - r = unit_realize_cgroup_now(u); - - return r; + return unit_realize_cgroup_now(u); } void unit_destroy_cgroup(Unit *u) { @@ -586,7 +660,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; }