cgroup: add support for memory.peak

Linux's Control Group v2 interfaces exposes memory.peak, which contains the
"max memory usage recorded for the cgroup and its descendants since the
creation of the cgroup."

This commit adds a new property "MemoryPeak" for units and makes "systemctl
show" display this value if it is available.

Fixes #29878.

Signed-off-by: Florian Schmaus <flo@geekplace.eu>
This commit is contained in:
Florian Schmaus 2023-11-06 13:15:55 +01:00
parent 69c37b26a4
commit 6c71db763c
8 changed files with 149 additions and 13 deletions

3
NEWS
View file

@ -130,6 +130,9 @@ CHANGES WITH 255 in spe:
machinectl bind and mount-image verbs will now cause the new mount to
replace the old mount (if any), instead of overmounting it.
* Units now have a MemoryPeak property, which contains the value of
cgroup v2's memory.peak property.
TPM2 Support + Disk Encryption & Authentication:
* systemd-cryptenroll now allows specifying a PCR bank and explicit hash

View file

@ -2776,6 +2776,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 MemoryPeak = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@ -3403,6 +3405,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property MemoryCurrent is not documented!-->
<!--property MemoryPeak is not documented!-->
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
@ -4035,6 +4039,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="MemoryPeak"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
@ -4831,6 +4837,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 MemoryPeak = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@ -5468,6 +5476,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property MemoryCurrent is not documented!-->
<!--property MemoryPeak is not documented!-->
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
@ -6082,6 +6092,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="MemoryPeak"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
@ -6752,6 +6764,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 MemoryPeak = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@ -7317,6 +7331,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property MemoryCurrent is not documented!-->
<!--property MemoryPeak is not documented!-->
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
@ -7845,6 +7861,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="MemoryPeak"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
@ -8638,6 +8656,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 MemoryPeak = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@ -9189,6 +9209,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property MemoryCurrent is not documented!-->
<!--property MemoryPeak is not documented!-->
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
@ -9703,6 +9725,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="MemoryPeak"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
@ -10355,6 +10379,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 MemoryPeak = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@ -10532,6 +10558,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<!--property MemoryCurrent is not documented!-->
<!--property MemoryPeak is not documented!-->
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
@ -10710,6 +10738,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="MemoryPeak"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
@ -10916,6 +10946,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 MemoryPeak = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@ -11113,6 +11145,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<!--property MemoryCurrent is not documented!-->
<!--property MemoryPeak is not documented!-->
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
@ -11321,6 +11355,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="MemoryPeak"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
@ -11716,8 +11752,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>MountImagePolicy</varname>, and
<varname>ExtensionImagePolicy</varname> were added in version 254.</para>
<para><varname>NFTSet</varname>,
<varname>SetLoginEnvironment</varname> and
<varname>CoredumpReceive</varname> were added in version 255.</para>
<varname>SetLoginEnvironment</varname>,
<varname>CoredumpReceive</varname>, and
<varname>MemoryPeak</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Socket Unit Objects</title>
@ -11743,8 +11780,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<para><varname>PollLimitIntervalUSec</varname>,
<varname>PollLimitBurst</varname>,
<varname>NFTSet</varname>,
<varname>SetLoginEnvironment</varname> and
<varname>CoredumpReceive</varname> were added in version 255.</para>
<varname>SetLoginEnvironment</varname>,
<varname>CoredumpReceive</varname>, and
<varname>MemoryPeak</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Mount Unit Objects</title>
@ -11768,8 +11806,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>MountImagePolicy</varname>, and
<varname>ExtensionImagePolicy</varname> were added in version 254.</para>
<para><varname>NFTSet</varname>,
<varname>SetLoginEnvironment</varname> and
<varname>CoredumpReceive</varname> were added in version 255.</para>
<varname>SetLoginEnvironment</varname>,
<varname>CoredumpReceive</varname>, and
<varname>MemoryPeak</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Swap Unit Objects</title>
@ -11793,8 +11832,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>MountImagePolicy</varname>, and
<varname>ExtensionImagePolicy</varname> were added in version 254.</para>
<para><varname>NFTSet</varname>,
<varname>SetLoginEnvironment</varname> and
<varname>CoredumpReceive</varname> were added in version 255.</para>
<varname>SetLoginEnvironment</varname>,
<varname>CoredumpReceive</varname>, and
<varname>MemoryPeak</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Slice Unit Objects</title>
@ -11809,8 +11849,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>StartupMemoryZSwapMax</varname>,
<varname>MemoryPressureWatch</varname>, and
<varname>MemoryPressureThresholdUSec</varname> were added in version 254.</para>
<para><varname>NFTSet</varname> and
<varname>CoredumpReceive</varname> were added in version 255.</para>
<para><varname>NFTSet</varname>,
<varname>CoredumpReceive</varname>, and
<varname>MemoryPeak</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Scope Unit Objects</title>
@ -11826,8 +11867,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>StartupMemoryZSwapMax</varname>,
<varname>MemoryPressureWatch</varname>, and
<varname>MemoryPressureThresholdUSec</varname> were added in version 254.</para>
<para><varname>NFTSet</varname> and
<varname>CoredumpReceive</varname> were added in version 255.</para>
<para><varname>NFTSet</varname>,
<varname>CoredumpReceive</varname>, and
<varname>MemoryPeak</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Job Objects</title>

View file

@ -4040,6 +4040,60 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
return cg_get_attribute_as_uint64("memory", u->cgroup_path, r > 0 ? "memory.current" : "memory.usage_in_bytes", ret);
}
static int unit_get_memory_peak_raw(Unit *u, uint64_t *ret) {
int r;
assert(u);
assert(ret);
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;
if (!r)
return -ENODATA;
return cg_get_attribute_as_uint64("memory", u->cgroup_path, "memory.peak", ret);
}
int unit_get_memory_peak(Unit *u, uint64_t *ret) {
uint64_t bytes;
int r;
assert(u);
assert(ret);
if (!UNIT_CGROUP_BOOL(u, memory_accounting))
return -ENODATA;
r = unit_get_memory_peak_raw(u, &bytes);
if (r == -ENODATA && u->memory_peak_last != UINT64_MAX) {
/* If we can't get the memory peak anymore (because the cgroup was already removed, for example),
* use our cached value. */
if (ret)
*ret = u->memory_peak_last;
return 0;
}
if (r < 0)
return r;
u->memory_peak_last = bytes;
if (ret)
*ret = bytes;
return 0;
}
int unit_get_tasks_current(Unit *u, uint64_t *ret) {
assert(u);
assert(ret);

View file

@ -353,6 +353,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_peak(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);

View file

@ -1080,6 +1080,29 @@ static int property_get_current_memory(
return sd_bus_message_append(reply, "t", sz);
}
static int property_get_peak_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 = ASSERT_PTR(userdata);
int r;
assert(bus);
assert(reply);
r = unit_get_memory_peak(u, &sz);
if (r < 0 && r != -ENODATA)
log_unit_warning_errno(u, r, "Failed to get memory.peak attribute: %m");
return sd_bus_message_append(reply, "t", sz);
}
static int property_get_available_memory(
sd_bus *bus,
const char *path,
@ -1536,6 +1559,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
SD_BUS_PROPERTY("ControlGroupId", "t", NULL, offsetof(Unit, cgroup_id), 0),
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
SD_BUS_PROPERTY("MemoryPeak", "t", property_get_peak_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),

View file

@ -114,6 +114,7 @@ Unit* unit_new(Manager *m, size_t size) {
u->ref_uid = UID_INVALID;
u->ref_gid = GID_INVALID;
u->cpu_usage_last = NSEC_INFINITY;
u->memory_peak_last = UINT64_MAX;
u->cgroup_invalidated_mask |= CGROUP_MASK_BPF_FIREWALL;
u->failure_action_exit_status = u->success_action_exit_status = -1;

View file

@ -365,6 +365,9 @@ typedef struct Unit {
nsec_t cpu_usage_base;
nsec_t cpu_usage_last; /* the most recently read value */
/* Most recently read value of memory.peak */
uint64_t memory_peak_last;
/* The current counter of OOM kills initiated by systemd-oomd */
uint64_t managed_oom_kill_last;

View file

@ -250,6 +250,7 @@ typedef struct UnitStatusInfo {
/* CGroup */
uint64_t memory_current;
uint64_t memory_peak;
uint64_t memory_min;
uint64_t memory_low;
uint64_t startup_memory_low;
@ -702,7 +703,8 @@ static void print_status_info(
if (i->memory_current != UINT64_MAX) {
printf(" Memory: %s", FORMAT_BYTES(i->memory_current));
if (i->memory_min > 0 ||
if (i->memory_peak != CGROUP_LIMIT_MAX ||
i->memory_min > 0 ||
i->memory_low > 0 || i->startup_memory_low > 0 ||
i->memory_high != CGROUP_LIMIT_MAX || i->startup_memory_high != CGROUP_LIMIT_MAX ||
i->memory_max != CGROUP_LIMIT_MAX || i->startup_memory_max != CGROUP_LIMIT_MAX ||
@ -765,6 +767,10 @@ static void print_status_info(
printf("%savailable: %s", prefix, FORMAT_BYTES(i->memory_available));
prefix = " ";
}
if (i->memory_peak != CGROUP_LIMIT_MAX) {
printf("%speak: %s", prefix, FORMAT_BYTES(i->memory_peak));
prefix = " ";
}
printf(")");
}
printf("\n");
@ -2031,6 +2037,7 @@ static int show_one(
{ "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
{ "What", "s", NULL, offsetof(UnitStatusInfo, what) },
{ "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
{ "MemoryPeak", "t", NULL, offsetof(UnitStatusInfo, memory_peak) },
{ "MemoryAvailable", "t", NULL, offsetof(UnitStatusInfo, memory_available) },
{ "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
{ "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
@ -2087,6 +2094,7 @@ static int show_one(
.memory_zswap_max = CGROUP_LIMIT_MAX,
.startup_memory_zswap_max = CGROUP_LIMIT_MAX,
.memory_limit = CGROUP_LIMIT_MAX,
.memory_peak = CGROUP_LIMIT_MAX,
.memory_available = CGROUP_LIMIT_MAX,
.cpu_usage_nsec = UINT64_MAX,
.tasks_current = UINT64_MAX,