diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 75d3d457bd3..4ca0583d655 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -4603,10 +4603,11 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { ExtensionDirectories see systemd.exec(5) for their meaning. - MemoryAvailable indicates how much unused memory is available to the unit before - the MemoryMax or MemoryHigh (whichever is lower) limit set by the cgroup - memory controller is reached. It will take into consideration limits on all parent slices, other than the - limits set on the unit itself. + MemoryAvailable takes into account unit's and parents' MemoryMax + or MemoryHigh or physically available RAM versus given level's memory consumption + and takes minimum. Beware that other units below the tightest parent slice may consume the memory quicker + and less than reported value would remain for own allocation. + It works better in conjunction with MemoryAccounting=yes on involved units. DelegateSubgroup contains the cgroup subgroup to place invoked unit processes in. As configured by the option of the same name in unit files. This is set to the empty string when it diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 91ef33b12c9..b304b39e8c1 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -3751,10 +3751,7 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { } int unit_get_memory_available(Unit *u, uint64_t *ret) { - uint64_t unit_current, available = UINT64_MAX; - CGroupContext *unit_context; - const char *memory_file; - int r; + uint64_t available = UINT64_MAX, current = 0; assert(u); assert(ret); @@ -3763,58 +3760,33 @@ int unit_get_memory_available(Unit *u, uint64_t *ret) { * claim before hitting the configured cgroup limits (if any). Consider both MemoryHigh * and MemoryMax, and also any slice the unit might be nested below. */ - if (!UNIT_CGROUP_BOOL(u, memory_accounting)) - return -ENODATA; - - if (!u->cgroup_path) - return -ENODATA; - - /* The root cgroup doesn't expose this information */ - if (unit_has_host_root_cgroup(u)) - return -ENODATA; - - if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0) - return -ENODATA; - - r = cg_all_unified(); - if (r < 0) - return r; - memory_file = r > 0 ? "memory.current" : "memory.usage_in_bytes"; - - r = cg_get_attribute_as_uint64("memory", u->cgroup_path, memory_file, &unit_current); - if (r < 0) - return r; - - assert_se(unit_context = unit_get_cgroup_context(u)); - - if (unit_context->memory_max != UINT64_MAX || unit_context->memory_high != UINT64_MAX) - available = LESS_BY(MIN(unit_context->memory_max, unit_context->memory_high), unit_current); - - for (Unit *slice = UNIT_GET_SLICE(u); slice; slice = UNIT_GET_SLICE(slice)) { - uint64_t slice_current, slice_available = UINT64_MAX; - CGroupContext *slice_context; + do { + uint64_t unit_available, unit_limit = UINT64_MAX; + CGroupContext *unit_context; /* No point in continuing if we can't go any lower */ if (available == 0) break; - if (!slice->cgroup_path) + unit_context = unit_get_cgroup_context(u); + if (!unit_context) + return -ENODATA; + + if (!u->cgroup_path) continue; - slice_context = unit_get_cgroup_context(slice); - if (!slice_context) - continue; + (void) unit_get_memory_current(u, ¤t); + /* in case of error, previous current propagates as lower bound */ - if (slice_context->memory_max == UINT64_MAX && slice_context->memory_high == UINT64_MAX) + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) + unit_limit = physical_memory(); + else if (unit_context->memory_max == UINT64_MAX && unit_context->memory_high == UINT64_MAX) continue; + unit_limit = MIN3(unit_limit, unit_context->memory_max, unit_context->memory_high); - r = cg_get_attribute_as_uint64("memory", slice->cgroup_path, memory_file, &slice_current); - if (r < 0) - continue; - - slice_available = LESS_BY(MIN(slice_context->memory_max, slice_context->memory_high), slice_current); - available = MIN(slice_available, available); - } + unit_available = LESS_BY(unit_limit, current); + available = MIN(unit_available, available); + } while ((u = UNIT_GET_SLICE(u))); *ret = available; diff --git a/src/shared/bus-print-properties.c b/src/shared/bus-print-properties.c index db41ad24957..8999a1a4fad 100644 --- a/src/shared/bus-print-properties.c +++ b/src/shared/bus-print-properties.c @@ -157,12 +157,12 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) || (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) || (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) || - (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == UINT64_MAX) || + (STR_IN_SET(name, "MemoryCurrent", "MemoryAvailable", "TasksCurrent") && u == UINT64_MAX) || (endswith(name, "NSec") && u == UINT64_MAX)) bus_print_property_value(name, expected_value, flags, "[not set]"); - else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit", "MemoryAvailable") && u == CGROUP_LIMIT_MAX) || + else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) || (startswith(name, "Limit") && u == UINT64_MAX) || (startswith(name, "DefaultLimit") && u == UINT64_MAX))