1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-09 12:58:26 +03:00

Merge pull request #29117 from Werkov/memory_available

Make MemoryAvailable= more useful
This commit is contained in:
Mike Yuan 2023-09-09 20:52:05 +08:00 committed by GitHub
commit 66e089af85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 52 deletions

View File

@ -4603,10 +4603,11 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<varname>ExtensionDirectories</varname>
see systemd.exec(5) for their meaning.</para>
<para><varname>MemoryAvailable</varname> indicates how much unused memory is available to the unit before
the <literal>MemoryMax</literal> or <literal>MemoryHigh</literal> (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.</para>
<para><varname>MemoryAvailable</varname> takes into account unit's and parents' <literal>MemoryMax</literal>
or <literal>MemoryHigh</literal> 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 <varname>MemoryAccounting=yes</varname> on involved units.</para>
<para><varname>DelegateSubgroup</varname> 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

View File

@ -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, &current);
/* 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;

View File

@ -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))