mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
core: add MemoryAvailable unit property
Try to infer the unused memory that a unit can claim before the memory.max limit is reached, including any limit set on any parent slice above the unit itself.
This commit is contained in:
parent
d477a094e8
commit
93ff34e44a
@ -2401,6 +2401,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -3504,6 +3506,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -4063,6 +4067,11 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
<varname>MountImages</varname>
|
||||
<varname>ExtensionImages</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>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
@ -4196,6 +4205,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -5321,6 +5332,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -5915,6 +5928,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -6886,6 +6901,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -7601,6 +7618,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -8544,6 +8563,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -9112,6 +9133,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -9403,6 +9426,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -9571,6 +9596,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -9904,6 +9931,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
|
@ -3402,6 +3402,77 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
assert(u);
|
||||
assert(ret);
|
||||
|
||||
/* If data from cgroups can be accessed, try to find out how much more memory a unit can
|
||||
* 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;
|
||||
|
||||
/* No point in continuing if we can't go any lower */
|
||||
if (available == 0)
|
||||
break;
|
||||
|
||||
if (!slice->cgroup_path)
|
||||
continue;
|
||||
|
||||
slice_context = unit_get_cgroup_context(slice);
|
||||
if (!slice_context)
|
||||
continue;
|
||||
|
||||
if (slice_context->memory_max == UINT64_MAX && slice_context->memory_high == UINT64_MAX)
|
||||
continue;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
*ret = available;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unit_get_memory_current(Unit *u, uint64_t *ret) {
|
||||
int r;
|
||||
|
||||
|
@ -282,6 +282,7 @@ int unit_watch_all_pids(Unit *u);
|
||||
int unit_synthesize_cgroup_empty_event(Unit *u);
|
||||
|
||||
int unit_get_memory_current(Unit *u, uint64_t *ret);
|
||||
int unit_get_memory_available(Unit *u, uint64_t *ret);
|
||||
int unit_get_tasks_current(Unit *u, uint64_t *ret);
|
||||
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
|
||||
int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
|
||||
|
@ -1097,6 +1097,30 @@ static int property_get_current_memory(
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_available_memory(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
uint64_t sz = UINT64_MAX;
|
||||
Unit *u = userdata;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
assert(u);
|
||||
|
||||
r = unit_get_memory_available(u, &sz);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_unit_warning_errno(u, r, "Failed to get total available memory from cgroup: %m");
|
||||
|
||||
return sd_bus_message_append(reply, "t", sz);
|
||||
}
|
||||
|
||||
static int property_get_current_tasks(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@ -1541,6 +1565,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
|
||||
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
|
||||
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
|
||||
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0),
|
||||
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
|
||||
SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
|
||||
SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
|
||||
|
@ -165,7 +165,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
|
||||
|
||||
bus_print_property_value(name, expected_value, flags, "[not set]");
|
||||
|
||||
else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
|
||||
else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "MemoryAvailable") && 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))
|
||||
|
@ -247,6 +247,7 @@ typedef struct UnitStatusInfo {
|
||||
uint64_t memory_max;
|
||||
uint64_t memory_swap_max;
|
||||
uint64_t memory_limit;
|
||||
uint64_t memory_available;
|
||||
uint64_t cpu_usage_nsec;
|
||||
uint64_t tasks_current;
|
||||
uint64_t tasks_max;
|
||||
@ -682,6 +683,7 @@ static void print_status_info(
|
||||
if (i->memory_min > 0 || i->memory_low > 0 ||
|
||||
i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
|
||||
i->memory_swap_max != CGROUP_LIMIT_MAX ||
|
||||
i->memory_available != CGROUP_LIMIT_MAX ||
|
||||
i->memory_limit != CGROUP_LIMIT_MAX) {
|
||||
const char *prefix = "";
|
||||
|
||||
@ -710,6 +712,10 @@ static void print_status_info(
|
||||
printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
|
||||
prefix = " ";
|
||||
}
|
||||
if (i->memory_available != CGROUP_LIMIT_MAX) {
|
||||
printf("%savailable: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_available));
|
||||
prefix = " ";
|
||||
}
|
||||
printf(")");
|
||||
}
|
||||
printf("\n");
|
||||
@ -1827,6 +1833,7 @@ static int show_one(
|
||||
{ "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
|
||||
{ "What", "s", NULL, offsetof(UnitStatusInfo, what) },
|
||||
{ "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
|
||||
{ "MemoryAvailable", "t", NULL, offsetof(UnitStatusInfo, memory_available) },
|
||||
{ "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
|
||||
{ "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
|
||||
{ "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
|
||||
@ -1869,6 +1876,7 @@ static int show_one(
|
||||
.memory_max = CGROUP_LIMIT_MAX,
|
||||
.memory_swap_max = CGROUP_LIMIT_MAX,
|
||||
.memory_limit = UINT64_MAX,
|
||||
.memory_available = CGROUP_LIMIT_MAX,
|
||||
.cpu_usage_nsec = UINT64_MAX,
|
||||
.tasks_current = UINT64_MAX,
|
||||
.tasks_max = UINT64_MAX,
|
||||
|
Loading…
Reference in New Issue
Block a user