diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index cbeb25efcd..027a8deeb4 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -552,6 +552,10 @@ node /org/freedesktop/systemd1 {
readonly t DefaultMemoryPressureThresholdUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s DefaultMemoryPressureWatch = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t DefaultCPUPressureThresholdUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly s DefaultCPUPressureWatch = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t TimerSlackNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -793,6 +797,10 @@ node /org/freedesktop/systemd1 {
+
+
+
+
@@ -1243,6 +1251,10 @@ node /org/freedesktop/systemd1 {
+
+
+
+
@@ -3066,6 +3078,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryPressureThresholdUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly s CPUPressureWatch = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t CPUPressureThresholdUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiss) NFTSet = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b CoredumpReceive = ...;
@@ -3735,6 +3751,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
+
+
@@ -4427,6 +4447,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
+
+
@@ -5326,6 +5350,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryPressureThresholdUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly s CPUPressureWatch = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t CPUPressureThresholdUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiss) NFTSet = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b CoredumpReceive = ...;
@@ -6011,6 +6039,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
+
+
@@ -6677,6 +6709,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
+
+
@@ -7399,6 +7435,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryPressureThresholdUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly s CPUPressureWatch = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t CPUPressureThresholdUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiss) NFTSet = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b CoredumpReceive = ...;
@@ -8008,6 +8048,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
+
+
@@ -8582,6 +8626,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
+
+
@@ -9437,6 +9485,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryPressureThresholdUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly s CPUPressureWatch = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t CPUPressureThresholdUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiss) NFTSet = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b CoredumpReceive = ...;
@@ -10028,6 +10080,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
+
+
@@ -10584,6 +10640,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
+
+
@@ -11292,6 +11352,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryPressureThresholdUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly s CPUPressureWatch = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t CPUPressureThresholdUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiss) NFTSet = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b CoredumpReceive = ...;
@@ -11465,6 +11529,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
+
+
+
+
@@ -11653,6 +11721,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
+
+
+
+
@@ -11864,6 +11936,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryPressureThresholdUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly s CPUPressureWatch = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t CPUPressureThresholdUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiss) NFTSet = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b CoredumpReceive = ...;
@@ -12051,6 +12127,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
+
+
+
+
@@ -12263,6 +12343,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
+
+
+
+
@@ -12475,7 +12559,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
RemoveSubgroupFromUnit(), and
KillUnitSubgroup() were added in version 258.
TransactionsWithOrderingCycle was added in version 259.
- DefaultMemoryZSwapWriteback was added in version 261.
+ DefaultMemoryZSwapWriteback,
+ DefaultCPUPressureThresholdUSec and
+ DefaultCPUPressureWatch were added in version 261.
Unit Objects
@@ -12567,6 +12653,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
ExecReloadPostEx were added in version 259.
BindNetworkInterface, MemoryTHP,
RefreshOnReload, and RootMStack were added in version 260.
+ CPUPressureThresholdUSec and
+ CPUPressureWatch were added in version 261.
Socket Unit Objects
@@ -12637,6 +12725,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
ManagedOOMKills were added in 259.
BindNetworkInterface MemoryTHP, and
RootMStack were added in version 260.
+ CPUPressureThresholdUSec and
+ CPUPressureWatch were added in version 261.
Mount Unit Objects
@@ -12702,6 +12792,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
ManagedOOMKills were added in 259.
BindNetworkInterface MemoryTHP, and
RootMStack were added in version 260.
+ CPUPressureThresholdUSec and
+ CPUPressureWatch were added in version 261.
Swap Unit Objects
@@ -12765,6 +12857,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
ManagedOOMKills were added in 259.
BindNetworkInterface, MemoryTHP, and
RootMStack were added in version 260.
+ CPUPressureThresholdUSec and
+ CPUPressureWatch were added in version 261.
Slice Unit Objects
@@ -12798,6 +12892,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
OOMKills, and
ManagedOOMKills were added in 259.
BindNetworkInterface was added in version 260.
+ CPUPressureThresholdUSec and
+ CPUPressureWatch were added in version 261.
Scope Unit Objects
@@ -12829,6 +12925,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
OOMKills, and
ManagedOOMKills were added in 259.
BindNetworkInterface was added in version 260.
+ CPUPressureThresholdUSec and
+ CPUPressureWatch were added in version 261.
Job Objects
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index e9e7d3d78d..79133dc15e 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -326,6 +326,20 @@
+
+
+ DefaultCPUPressureWatch=
+ DefaultCPUPressureThresholdSec=
+
+ Configures the default settings for the per-unit
+ CPUPressureWatch= and CPUPressureThresholdSec=
+ settings. See
+ systemd.resource-control5
+ for details. Defaults to auto and 200ms, respectively. This
+ also sets the CPU pressure monitoring threshold for the service manager itself.
+
+
+
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 48bec7361b..1048fcadfc 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -4705,6 +4705,18 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
+
+ $CPU_PRESSURE_WATCH
+ $CPU_PRESSURE_WRITE
+
+ If CPU pressure monitoring is enabled for this service unit, the path to watch
+ and the data to write into it. See Resource Pressure
+ Handling for details about these variables and the service protocol data they
+ convey.
+
+
+
+
$FDSTORE
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
index ac31971e54..8d9e27f3d3 100644
--- a/man/systemd.resource-control.xml
+++ b/man/systemd.resource-control.xml
@@ -1657,6 +1657,55 @@ DeviceAllow=/dev/loop-control
+
+
+ CPUPressureWatch=
+
+ Controls CPU pressure monitoring for invoked processes. Takes a boolean or one of
+ auto and skip. If no, tells the service not
+ to watch for CPU pressure events, by setting the $CPU_PRESSURE_WATCH
+ environment variable to the literal string /dev/null. If yes,
+ tells the service to watch for CPU pressure events. This ensures the
+ cpu.pressure cgroup attribute file is accessible for
+ reading and writing by the service's user. It then sets the $CPU_PRESSURE_WATCH
+ environment variable for processes invoked by the unit to the file system path to this file. The
+ threshold information configured with CPUPressureThresholdSec= is encoded in
+ the $CPU_PRESSURE_WRITE environment variable. If the auto
+ value is set the protocol is enabled if CPU resource controls are configured for the unit (e.g. because
+ CPUWeight= or CPUQuota= is set), and
+ disabled otherwise. If set to skip the logic is neither enabled, nor disabled and
+ the two environment variables are not set.
+
+ Note that services are free to use the two environment variables, but it is unproblematic if
+ they ignore them. CPU pressure handling must be implemented individually in each service, and
+ usually means different things for different software.
+
+ Services implemented using
+ sd-event3 may use
+ sd_event_add_cpu_pressure3
+ to watch for and handle CPU pressure events.
+
+ If not explicitly set, defaults to the DefaultCPUPressureWatch= setting in
+ systemd-system.conf5.
+
+
+
+
+
+ CPUPressureThresholdSec=
+
+ Sets the CPU pressure threshold time for CPU pressure monitor as configured via
+ CPUPressureWatch=. Specifies the maximum CPU stall time before a CPU
+ pressure event is signalled to the service, per 2s window. If not specified, defaults to the
+ DefaultCPUPressureThresholdSec= setting in
+ systemd-system.conf5
+ (which in turn defaults to 200ms). The specified value expects a time unit such as
+ ms or μs, see
+ systemd.time7 for
+ details on the permitted syntax.
+
+
+
Coredump Control
diff --git a/src/basic/psi-util.h b/src/basic/psi-util.h
index aed74ef742..b8737b8976 100644
--- a/src/basic/psi-util.h
+++ b/src/basic/psi-util.h
@@ -43,6 +43,11 @@ typedef struct PressureResourceInfo {
extern const PressureResourceInfo pressure_resource_info[_PRESSURE_RESOURCE_MAX];
+static inline const PressureResourceInfo* pressure_resource_get_info(PressureResource resource) {
+ assert(resource >= 0 && resource < _PRESSURE_RESOURCE_MAX);
+ return &pressure_resource_info[resource];
+}
+
DECLARE_STRING_TABLE_LOOKUP(pressure_resource, PressureResource);
/* Default parameters for pressure watch logic in sd-event and PID 1 */
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 7bcd6777df..a9982de659 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -185,8 +185,10 @@ void cgroup_context_init(CGroupContext *c) {
* moom_mem_pressure_duration_usec is set to infinity. */
.moom_mem_pressure_duration_usec = USEC_INFINITY,
- .memory_pressure_watch = _CGROUP_PRESSURE_WATCH_INVALID,
- .memory_pressure_threshold_usec = USEC_INFINITY,
+ .pressure = {
+ [PRESSURE_MEMORY] = { .watch = _CGROUP_PRESSURE_WATCH_INVALID, .threshold_usec = USEC_INFINITY },
+ [PRESSURE_CPU] = { .watch = _CGROUP_PRESSURE_WATCH_INVALID, .threshold_usec = USEC_INFINITY },
+ },
};
}
@@ -528,6 +530,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
"%sManagedOOMMemoryPressureLimit: " PERMYRIAD_AS_PERCENT_FORMAT_STR "\n"
"%sManagedOOMPreference: %s\n"
"%sMemoryPressureWatch: %s\n"
+ "%sCPUPressureWatch: %s\n"
"%sCoredumpReceive: %s\n",
prefix, yes_no(c->io_accounting),
prefix, yes_no(c->memory_accounting),
@@ -563,7 +566,8 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
prefix, managed_oom_mode_to_string(c->moom_mem_pressure),
prefix, PERMYRIAD_AS_PERCENT_FORMAT_VAL(UINT32_SCALE_TO_PERMYRIAD(c->moom_mem_pressure_limit)),
prefix, managed_oom_preference_to_string(c->moom_preference),
- prefix, cgroup_pressure_watch_to_string(c->memory_pressure_watch),
+ prefix, cgroup_pressure_watch_to_string(c->pressure[PRESSURE_MEMORY].watch),
+ prefix, cgroup_pressure_watch_to_string(c->pressure[PRESSURE_CPU].watch),
prefix, yes_no(c->coredump_receive));
if (c->delegate_subgroup)
@@ -574,9 +578,13 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
fprintf(f, "%sBindNetworkInterface: %s\n",
prefix, c->bind_network_interface);
- if (c->memory_pressure_threshold_usec != USEC_INFINITY)
+ if (c->pressure[PRESSURE_MEMORY].threshold_usec != USEC_INFINITY)
fprintf(f, "%sMemoryPressureThresholdSec: %s\n",
- prefix, FORMAT_TIMESPAN(c->memory_pressure_threshold_usec, 1));
+ prefix, FORMAT_TIMESPAN(c->pressure[PRESSURE_MEMORY].threshold_usec, 1));
+
+ if (c->pressure[PRESSURE_CPU].threshold_usec != USEC_INFINITY)
+ fprintf(f, "%sCPUPressureThresholdSec: %s\n",
+ prefix, FORMAT_TIMESPAN(c->pressure[PRESSURE_CPU].threshold_usec, 1));
if (c->moom_mem_pressure_duration_usec != USEC_INFINITY)
fprintf(f, "%sManagedOOMMemoryPressureDurationSec: %s\n",
@@ -2107,12 +2115,13 @@ static int unit_update_cgroup(
cgroup_context_apply(u, target_mask, state);
cgroup_xattr_apply(u);
- /* For most units we expect that memory monitoring is set up before the unit is started and we won't
- * touch it after. For PID 1 this is different though, because we couldn't possibly do that given
- * that PID 1 runs before init.scope is even set up. Hence, whenever init.scope is realized, let's
- * try to open the memory pressure interface anew. */
+ /* For most units we expect that pressure monitoring is set up before the unit is started and we
+ * won't touch it after. For PID 1 this is different though, because we couldn't possibly do that
+ * given that PID 1 runs before init.scope is even set up. Hence, whenever init.scope is realized,
+ * let's try to open the pressure interfaces anew. */
if (unit_has_name(u, SPECIAL_INIT_SCOPE))
- (void) manager_setup_memory_pressure_event_source(u->manager);
+ for (PressureResource t = 0; t < _PRESSURE_RESOURCE_MAX; t++)
+ (void) manager_setup_pressure_event_source(u->manager, t);
return 0;
}
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 0cd290e92f..c4a2276567 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -6,6 +6,7 @@
#include "cpu-set-util.h"
#include "firewall-util.h"
#include "list.h"
+#include "psi-util.h"
typedef struct CGroupTasksMax {
/* If scale == 0, just use value; otherwise, value / scale.
@@ -95,14 +96,19 @@ typedef struct CGroupSocketBindItem {
} CGroupSocketBindItem;
typedef enum CGroupPressureWatch {
- CGROUP_PRESSURE_WATCH_NO, /* → tells the service payload explicitly not to watch for memory pressure */
+ CGROUP_PRESSURE_WATCH_NO, /* → tells the service payload explicitly not to watch for pressure */
CGROUP_PRESSURE_WATCH_YES,
- CGROUP_PRESSURE_WATCH_AUTO, /* → on if memory account is on anyway for the unit, otherwise off */
- CGROUP_PRESSURE_WATCH_SKIP, /* → doesn't set up memory pressure watch, but also doesn't explicitly tell payload to avoid it */
+ CGROUP_PRESSURE_WATCH_AUTO, /* → on if relevant accounting is on anyway for the unit, otherwise off */
+ CGROUP_PRESSURE_WATCH_SKIP, /* → doesn't set up pressure watch, but also doesn't explicitly tell payload to avoid it */
_CGROUP_PRESSURE_WATCH_MAX,
_CGROUP_PRESSURE_WATCH_INVALID = -EINVAL,
} CGroupPressureWatch;
+typedef struct CGroupPressure {
+ CGroupPressureWatch watch;
+ usec_t threshold_usec;
+} CGroupPressure;
+
/* The user-supplied cgroup-related configuration options. This remains mostly immutable while the service
* manager is running (except for an occasional SetProperties() configuration change), outside of reload
* cycles. */
@@ -189,11 +195,8 @@ typedef struct CGroupContext {
usec_t moom_mem_pressure_duration_usec;
ManagedOOMPreference moom_preference;
- /* Memory pressure logic */
- CGroupPressureWatch memory_pressure_watch;
- usec_t memory_pressure_threshold_usec;
- /* NB: For now we don't make the period configurable, not the type, nor do we allow multiple
- * triggers, nor triggers for non-memory pressure. We might add that later. */
+ /* Pressure logic */
+ CGroupPressure pressure[_PRESSURE_RESOURCE_MAX];
NFTSetContext nft_set_context;
@@ -353,11 +356,29 @@ void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLaten
void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p);
void cgroup_context_remove_socket_bind(CGroupSocketBindItem **head);
-static inline bool cgroup_context_want_memory_pressure(const CGroupContext *c) {
+static inline bool cgroup_context_want_pressure(const CGroupContext *c, PressureResource t) {
assert(c);
+ assert(t >= 0 && t < _PRESSURE_RESOURCE_MAX);
- return c->memory_pressure_watch == CGROUP_PRESSURE_WATCH_YES ||
- (c->memory_pressure_watch == CGROUP_PRESSURE_WATCH_AUTO && c->memory_accounting);
+ if (c->pressure[t].watch == CGROUP_PRESSURE_WATCH_YES)
+ return true;
+
+ if (c->pressure[t].watch != CGROUP_PRESSURE_WATCH_AUTO)
+ return false;
+
+ switch (t) {
+
+ case PRESSURE_MEMORY:
+ return c->memory_accounting;
+
+ case PRESSURE_CPU:
+ return c->cpu_weight != CGROUP_WEIGHT_INVALID ||
+ c->startup_cpu_weight != CGROUP_WEIGHT_INVALID ||
+ c->cpu_quota_per_sec_usec != USEC_INFINITY;
+
+ default:
+ assert_not_reached();
+ }
}
static inline bool cgroup_context_has_device_policy(const CGroupContext *c) {
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index a7508c96da..c5a3302e08 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -427,8 +427,10 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("SocketBindDeny", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_deny), 0),
SD_BUS_PROPERTY("RestrictNetworkInterfaces", "(bas)", property_get_restrict_network_interfaces, 0, 0),
SD_BUS_PROPERTY("BindNetworkInterface", "s", NULL, offsetof(CGroupContext, bind_network_interface), 0),
- SD_BUS_PROPERTY("MemoryPressureWatch", "s", bus_property_get_cgroup_pressure_watch, offsetof(CGroupContext, memory_pressure_watch), 0),
- SD_BUS_PROPERTY("MemoryPressureThresholdUSec", "t", bus_property_get_usec, offsetof(CGroupContext, memory_pressure_threshold_usec), 0),
+ SD_BUS_PROPERTY("MemoryPressureWatch", "s", bus_property_get_cgroup_pressure_watch, offsetof(CGroupContext, pressure[PRESSURE_MEMORY].watch), 0),
+ SD_BUS_PROPERTY("MemoryPressureThresholdUSec", "t", bus_property_get_usec, offsetof(CGroupContext, pressure[PRESSURE_MEMORY].threshold_usec), 0),
+ SD_BUS_PROPERTY("CPUPressureWatch", "s", bus_property_get_cgroup_pressure_watch, offsetof(CGroupContext, pressure[PRESSURE_CPU].watch), 0),
+ SD_BUS_PROPERTY("CPUPressureThresholdUSec", "t", bus_property_get_usec, offsetof(CGroupContext, pressure[PRESSURE_CPU].threshold_usec), 0),
SD_BUS_PROPERTY("NFTSet", "a(iiss)", property_get_cgroup_nft_set, 0, 0),
SD_BUS_PROPERTY("CoredumpReceive", "b", bus_property_get_bool, offsetof(CGroupContext, coredump_receive), 0),
@@ -712,10 +714,12 @@ static int bus_cgroup_set_transient_property(
return 1;
- } else if (streq(name, "MemoryPressureWatch")) {
+ } else if (STR_IN_SET(name, "MemoryPressureWatch", "CPUPressureWatch")) {
CGroupPressureWatch p;
const char *t;
+ PressureResource pt = streq(name, "MemoryPressureWatch") ? PRESSURE_MEMORY : PRESSURE_CPU;
+
r = sd_bus_message_read(message, "s", &t);
if (r < 0)
return r;
@@ -729,26 +733,28 @@ static int bus_cgroup_set_transient_property(
}
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->memory_pressure_watch = p;
- unit_write_settingf(u, flags, name, "MemoryPressureWatch=%s", strempty(cgroup_pressure_watch_to_string(p)));
+ c->pressure[pt].watch = p;
+ unit_write_settingf(u, flags, name, "%s=%s", name, strempty(cgroup_pressure_watch_to_string(p)));
}
return 1;
- } else if (streq(name, "MemoryPressureThresholdUSec")) {
+ } else if (STR_IN_SET(name, "MemoryPressureThresholdUSec", "CPUPressureThresholdUSec")) {
uint64_t t;
+ PressureResource pt = streq(name, "MemoryPressureThresholdUSec") ? PRESSURE_MEMORY : PRESSURE_CPU;
+
r = sd_bus_message_read(message, "t", &t);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->memory_pressure_threshold_usec = t;
+ c->pressure[pt].threshold_usec = t;
if (t == UINT64_MAX)
- unit_write_setting(u, flags, name, "MemoryPressureThresholdUSec=");
+ unit_write_settingf(u, flags, name, "%s=", name);
else
- unit_write_settingf(u, flags, name, "MemoryPressureThresholdUSec=%" PRIu64, t);
+ unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, t);
}
return 1;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 749e2261af..23f4c4c3de 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -2980,8 +2980,10 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultLimitRTTIME", "t", bus_property_get_rlimit, offsetof(Manager, defaults.rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultLimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(Manager, defaults.rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTasksMax", "t", bus_property_get_tasks_max, offsetof(Manager, defaults.tasks_max), 0),
- SD_BUS_PROPERTY("DefaultMemoryPressureThresholdUSec", "t", bus_property_get_usec, offsetof(Manager, defaults.memory_pressure_threshold_usec), 0),
- SD_BUS_PROPERTY("DefaultMemoryPressureWatch", "s", bus_property_get_cgroup_pressure_watch, offsetof(Manager, defaults.memory_pressure_watch), 0),
+ SD_BUS_PROPERTY("DefaultMemoryPressureThresholdUSec", "t", bus_property_get_usec, offsetof(Manager, defaults.pressure[PRESSURE_MEMORY].threshold_usec), 0),
+ SD_BUS_PROPERTY("DefaultMemoryPressureWatch", "s", bus_property_get_cgroup_pressure_watch, offsetof(Manager, defaults.pressure[PRESSURE_MEMORY].watch), 0),
+ SD_BUS_PROPERTY("DefaultCPUPressureThresholdUSec", "t", bus_property_get_usec, offsetof(Manager, defaults.pressure[PRESSURE_CPU].threshold_usec), 0),
+ SD_BUS_PROPERTY("DefaultCPUPressureWatch", "s", bus_property_get_cgroup_pressure_watch, offsetof(Manager, defaults.pressure[PRESSURE_CPU].watch), 0),
SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, defaults.oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultOOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
index 7500888c41..1361038af8 100644
--- a/src/core/exec-invoke.c
+++ b/src/core/exec-invoke.c
@@ -2034,7 +2034,7 @@ static int build_environment(
const char *shell,
dev_t journal_stream_dev,
ino_t journal_stream_ino,
- const char *memory_pressure_path,
+ char *const *pressure_path,
bool needs_sandboxing,
char ***ret) {
@@ -2215,25 +2215,38 @@ static int build_environment(
if (r < 0)
return r;
- if (memory_pressure_path) {
- r = strv_extend_joined_with_size(&e, &n, "MEMORY_PRESSURE_WATCH=", memory_pressure_path);
+ for (PressureResource t = 0; t < _PRESSURE_RESOURCE_MAX; t++) {
+ if (!pressure_path[t])
+ continue;
+
+ const PressureResourceInfo *info = pressure_resource_get_info(t);
+
+ _cleanup_free_ char *env_watch = strjoin(info->env_watch, "=");
+ if (!env_watch)
+ return -ENOMEM;
+
+ r = strv_extend_joined_with_size(&e, &n, env_watch, pressure_path[t]);
if (r < 0)
return r;
- if (!path_equal(memory_pressure_path, "/dev/null")) {
+ if (!path_equal(pressure_path[t], "/dev/null")) {
_cleanup_free_ char *b = NULL, *x = NULL;
if (asprintf(&b, "%s " USEC_FMT " " USEC_FMT,
PRESSURE_DEFAULT_TYPE,
- cgroup_context->memory_pressure_threshold_usec == USEC_INFINITY ? PRESSURE_DEFAULT_THRESHOLD_USEC :
- CLAMP(cgroup_context->memory_pressure_threshold_usec, 1U, PRESSURE_DEFAULT_WINDOW_USEC),
+ cgroup_context->pressure[t].threshold_usec == USEC_INFINITY ? PRESSURE_DEFAULT_THRESHOLD_USEC :
+ CLAMP(cgroup_context->pressure[t].threshold_usec, 1U, PRESSURE_DEFAULT_WINDOW_USEC),
PRESSURE_DEFAULT_WINDOW_USEC) < 0)
return -ENOMEM;
if (base64mem(b, strlen(b) + 1, &x) < 0)
return -ENOMEM;
- r = strv_extend_joined_with_size(&e, &n, "MEMORY_PRESSURE_WRITE=", x);
+ _cleanup_free_ char *env_write = strjoin(info->env_write, "=");
+ if (!env_write)
+ return -ENOMEM;
+
+ r = strv_extend_joined_with_size(&e, &n, env_write, x);
if (r < 0)
return r;
}
@@ -3855,7 +3868,7 @@ static int apply_mount_namespace(
const ExecParameters *params,
const ExecRuntime *runtime,
const PinnedResource *rootfs,
- const char *memory_pressure_path,
+ char *const *pressure_path,
bool needs_sandboxing,
uid_t exec_directory_uid,
gid_t exec_directory_gid,
@@ -3887,16 +3900,28 @@ static int apply_mount_namespace(
if (r < 0)
return r;
- /* We need to make the pressure path writable even if /sys/fs/cgroups is made read-only, as the
- * service will need to write to it in order to start the notifications. */
- if (exec_is_cgroup_mount_read_only(context) && memory_pressure_path && !streq(memory_pressure_path, "/dev/null")) {
+ /* We need to make the pressure paths writable even if /sys/fs/cgroups is made read-only, as the
+ * service will need to write to them in order to start the notifications. */
+ bool need_pressure_rw = false;
+ for (PressureResource t = 0; t < _PRESSURE_RESOURCE_MAX; t++)
+ if (pressure_path[t] && !streq(pressure_path[t], "/dev/null")) {
+ need_pressure_rw = true;
+ break;
+ }
+
+ if (exec_is_cgroup_mount_read_only(context) && need_pressure_rw) {
read_write_paths_cleanup = strv_copy(context->read_write_paths);
if (!read_write_paths_cleanup)
return -ENOMEM;
- r = strv_extend(&read_write_paths_cleanup, memory_pressure_path);
- if (r < 0)
- return r;
+ for (PressureResource t = 0; t < _PRESSURE_RESOURCE_MAX; t++) {
+ if (!pressure_path[t] || streq(pressure_path[t], "/dev/null"))
+ continue;
+
+ r = strv_extend(&read_write_paths_cleanup, pressure_path[t]);
+ if (r < 0)
+ return r;
+ }
read_write_paths = read_write_paths_cleanup;
} else
@@ -4689,7 +4714,7 @@ static int setup_delegated_namespaces(
const ExecRuntime *runtime,
const PinnedResource *rootfs,
bool delegate,
- const char *memory_pressure_path,
+ char *const *pressure_path,
uid_t uid,
gid_t gid,
const ExecCommand *command,
@@ -4820,7 +4845,7 @@ static int setup_delegated_namespaces(
params,
runtime,
rootfs,
- memory_pressure_path,
+ pressure_path,
needs_sandboxing,
uid,
gid,
@@ -5146,6 +5171,10 @@ static int setup_term_environment(const ExecContext *context, char ***env) {
return strv_env_replace_strdup(env, "TERM=" FALLBACK_TERM);
}
+static inline void free_pressure_paths(char *(*p)[_PRESSURE_RESOURCE_MAX]) {
+ free_many_charp(*p, _PRESSURE_RESOURCE_MAX);
+}
+
int exec_invoke(
const ExecCommand *command,
const ExecContext *context,
@@ -5157,7 +5186,8 @@ int exec_invoke(
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **joined_exec_search_path = NULL, **accum_env = NULL;
int r;
const char *username = NULL, *groupname = NULL;
- _cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *own_user = NULL;
+ _cleanup_free_ char *home_buffer = NULL, *own_user = NULL;
+ _cleanup_(free_pressure_paths) char *pressure_path[_PRESSURE_RESOURCE_MAX] = {};
const char *pwent_home = NULL, *shell = NULL;
dev_t journal_stream_dev = 0;
ino_t journal_stream_ino = 0;
@@ -5753,36 +5783,44 @@ int exec_invoke(
}
if (is_pressure_supported() > 0) {
- if (cgroup_context_want_memory_pressure(cgroup_context)) {
- r = cg_get_path(params->cgroup_path, "memory.pressure", &memory_pressure_path);
- if (r < 0) {
- *exit_status = EXIT_MEMORY;
- return log_oom();
- }
+ for (PressureResource t = 0; t < _PRESSURE_RESOURCE_MAX; t++) {
+ if (cgroup_context_want_pressure(cgroup_context, t)) {
+ _cleanup_free_ char *pressure_file = strjoin(pressure_resource_to_string(t), ".pressure");
+ if (!pressure_file) {
+ *exit_status = EXIT_MEMORY;
+ return log_oom();
+ }
- r = chmod_and_chown(memory_pressure_path, 0644, uid, gid);
- if (r < 0) {
- log_full_errno(r == -ENOENT || ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to adjust ownership of '%s', ignoring: %m", memory_pressure_path);
- memory_pressure_path = mfree(memory_pressure_path);
- }
- /* First we use the current cgroup path to chmod and chown the memory pressure path, then pass the path relative
- * to the cgroup namespace to environment variables and mounts. If chown/chmod fails, we should not pass memory
- * pressure path environment variable or read-write mount to the unit. This is why we check if
- * memory_pressure_path != NULL in the conditional below. */
- if (memory_pressure_path && needs_sandboxing && exec_needs_cgroup_namespace(context)) {
- memory_pressure_path = mfree(memory_pressure_path);
- r = cg_get_path("/", "memory.pressure", &memory_pressure_path);
+ r = cg_get_path(params->cgroup_path, pressure_file, &pressure_path[t]);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return log_oom();
}
- }
- } else if (cgroup_context->memory_pressure_watch == CGROUP_PRESSURE_WATCH_NO) {
- memory_pressure_path = strdup("/dev/null"); /* /dev/null is explicit indicator for turning of memory pressure watch */
- if (!memory_pressure_path) {
- *exit_status = EXIT_MEMORY;
- return log_oom();
+
+ r = chmod_and_chown(pressure_path[t], 0644, uid, gid);
+ if (r < 0) {
+ log_full_errno(r == -ENOENT || ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to adjust ownership of '%s', ignoring: %m", pressure_path[t]);
+ pressure_path[t] = mfree(pressure_path[t]);
+ }
+ /* First we use the current cgroup path to chmod and chown the pressure path, then pass the
+ * path relative to the cgroup namespace to environment variables and mounts. If chown/chmod
+ * fails, we should not pass pressure path environment variable or read-write mount to the
+ * unit. This is why we check if pressure_path[t] != NULL in the conditional below. */
+ if (pressure_path[t] && needs_sandboxing && exec_needs_cgroup_namespace(context)) {
+ pressure_path[t] = mfree(pressure_path[t]);
+ r = cg_get_path("/", pressure_file, &pressure_path[t]);
+ if (r < 0) {
+ *exit_status = EXIT_MEMORY;
+ return log_oom();
+ }
+ }
+ } else if (cgroup_context->pressure[t].watch == CGROUP_PRESSURE_WATCH_NO) {
+ pressure_path[t] = strdup("/dev/null"); /* /dev/null is explicit indicator for turning off pressure watch */
+ if (!pressure_path[t]) {
+ *exit_status = EXIT_MEMORY;
+ return log_oom();
+ }
}
}
}
@@ -5829,7 +5867,7 @@ int exec_invoke(
shell,
journal_stream_dev,
journal_stream_ino,
- memory_pressure_path,
+ pressure_path,
needs_sandboxing,
&our_env);
if (r < 0) {
@@ -6047,7 +6085,7 @@ int exec_invoke(
runtime,
&rootfs,
/* delegate= */ false,
- memory_pressure_path,
+ pressure_path,
uid,
gid,
command,
@@ -6144,7 +6182,7 @@ int exec_invoke(
runtime,
&rootfs,
/* delegate= */ true,
- memory_pressure_path,
+ pressure_path,
uid,
gid,
command,
diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c
index c8f802687e..d3d23500a9 100644
--- a/src/core/execute-serialize.c
+++ b/src/core/execute-serialize.c
@@ -279,7 +279,11 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) {
if (r < 0)
return r;
- r = serialize_item(f, "exec-cgroup-context-memory-pressure-watch", cgroup_pressure_watch_to_string(c->memory_pressure_watch));
+ r = serialize_item(f, "exec-cgroup-context-memory-pressure-watch", cgroup_pressure_watch_to_string(c->pressure[PRESSURE_MEMORY].watch));
+ if (r < 0)
+ return r;
+
+ r = serialize_item(f, "exec-cgroup-context-cpu-pressure-watch", cgroup_pressure_watch_to_string(c->pressure[PRESSURE_CPU].watch));
if (r < 0)
return r;
@@ -287,8 +291,14 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) {
if (r < 0)
return r;
- if (c->memory_pressure_threshold_usec != USEC_INFINITY) {
- r = serialize_usec(f, "exec-cgroup-context-memory-pressure-threshold-usec", c->memory_pressure_threshold_usec);
+ if (c->pressure[PRESSURE_MEMORY].threshold_usec != USEC_INFINITY) {
+ r = serialize_usec(f, "exec-cgroup-context-memory-pressure-threshold-usec", c->pressure[PRESSURE_MEMORY].threshold_usec);
+ if (r < 0)
+ return r;
+ }
+
+ if (c->pressure[PRESSURE_CPU].threshold_usec != USEC_INFINITY) {
+ r = serialize_usec(f, "exec-cgroup-context-cpu-pressure-threshold-usec", c->pressure[PRESSURE_CPU].threshold_usec);
if (r < 0)
return r;
}
@@ -621,15 +631,23 @@ static int exec_cgroup_context_deserialize(CGroupContext *c, FILE *f) {
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-cgroup-context-memory-pressure-watch="))) {
- c->memory_pressure_watch = cgroup_pressure_watch_from_string(val);
- if (c->memory_pressure_watch < 0)
+ c->pressure[PRESSURE_MEMORY].watch = cgroup_pressure_watch_from_string(val);
+ if (c->pressure[PRESSURE_MEMORY].watch < 0)
+ return -EINVAL;
+ } else if ((val = startswith(l, "exec-cgroup-context-cpu-pressure-watch="))) {
+ c->pressure[PRESSURE_CPU].watch = cgroup_pressure_watch_from_string(val);
+ if (c->pressure[PRESSURE_CPU].watch < 0)
return -EINVAL;
} else if ((val = startswith(l, "exec-cgroup-context-delegate-subgroup="))) {
r = free_and_strdup(&c->delegate_subgroup, val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-cgroup-context-memory-pressure-threshold-usec="))) {
- r = deserialize_usec(val, &c->memory_pressure_threshold_usec);
+ r = deserialize_usec(val, &c->pressure[PRESSURE_MEMORY].threshold_usec);
+ if (r < 0)
+ return r;
+ } else if ((val = startswith(l, "exec-cgroup-context-cpu-pressure-threshold-usec="))) {
+ r = deserialize_usec(val, &c->pressure[PRESSURE_CPU].threshold_usec);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-cgroup-context-device-allow="))) {
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index bf808d220b..297836def1 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -276,8 +276,10 @@
{{type}}.SocketBindAllow, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_allow)
{{type}}.SocketBindDeny, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_deny)
{{type}}.RestrictNetworkInterfaces, config_parse_restrict_network_interfaces, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemoryPressureThresholdSec, config_parse_sec, 0, offsetof({{type}}, cgroup_context.memory_pressure_threshold_usec)
-{{type}}.MemoryPressureWatch, config_parse_memory_pressure_watch, 0, offsetof({{type}}, cgroup_context.memory_pressure_watch)
+{{type}}.MemoryPressureThresholdSec, config_parse_sec, 0, offsetof({{type}}, cgroup_context.pressure[PRESSURE_MEMORY].threshold_usec)
+{{type}}.MemoryPressureWatch, config_parse_pressure_watch, 0, offsetof({{type}}, cgroup_context.pressure[PRESSURE_MEMORY].watch)
+{{type}}.CPUPressureThresholdSec, config_parse_sec, 0, offsetof({{type}}, cgroup_context.pressure[PRESSURE_CPU].threshold_usec)
+{{type}}.CPUPressureWatch, config_parse_pressure_watch, 0, offsetof({{type}}, cgroup_context.pressure[PRESSURE_CPU].watch)
{{type}}.NFTSet, config_parse_cgroup_nft_set, NFT_SET_PARSE_CGROUP, offsetof({{type}}, cgroup_context)
{{type}}.CoredumpReceive, config_parse_bool, 0, offsetof({{type}}, cgroup_context.coredump_receive)
{{type}}.BindNetworkInterface, config_parse_bind_network_interface, 0, offsetof({{type}}, cgroup_context)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 840804fcf8..16f3afafe9 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -154,7 +154,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_time
DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only);
DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy);
DEFINE_CONFIG_PARSE_ENUM(config_parse_managed_oom_preference, managed_oom_preference, ManagedOOMPreference);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_memory_pressure_watch, cgroup_pressure_watch, CGroupPressureWatch);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_pressure_watch, cgroup_pressure_watch, CGroupPressureWatch);
DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1);
DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t);
DEFINE_CONFIG_PARSE_PTR(config_parse_cg_cpu_weight, cg_cpu_weight_parse, uint64_t);
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 4677564904..a5b7595dbf 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -164,7 +164,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_watchdog_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_tty_size);
CONFIG_PARSER_PROTOTYPE(config_parse_log_filter_patterns);
CONFIG_PARSER_PROTOTYPE(config_parse_open_file);
-CONFIG_PARSER_PROTOTYPE(config_parse_memory_pressure_watch);
+CONFIG_PARSER_PROTOTYPE(config_parse_pressure_watch);
CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_nft_set);
CONFIG_PARSER_PROTOTYPE(config_parse_mount_node);
CONFIG_PARSER_PROTOTYPE(config_parse_concurrency_max);
diff --git a/src/core/main.c b/src/core/main.c
index d0c0023f89..7fcd0fa672 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -747,89 +747,91 @@ static int config_parse_crash_reboot(
static int parse_config_file(void) {
const ConfigTableItem items[] = {
- { "Manager", "LogLevel", config_parse_level2, 0, NULL },
- { "Manager", "LogTarget", config_parse_target, 0, NULL },
- { "Manager", "LogColor", config_parse_color, 0, NULL },
- { "Manager", "LogLocation", config_parse_location, 0, NULL },
- { "Manager", "LogTime", config_parse_time, 0, NULL },
- { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core },
- { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, &arg_crash_chvt },
- { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, &arg_crash_chvt },
- { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell },
- { "Manager", "CrashReboot", config_parse_crash_reboot, 0, &arg_crash_action },
- { "Manager", "CrashAction", config_parse_crash_action, 0, &arg_crash_action },
- { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status },
- { "Manager", "StatusUnitFormat", config_parse_status_unit_format, 0, &arg_status_unit_format },
- { "Manager", "CPUAffinity", config_parse_cpu_set, 0, &arg_cpu_affinity },
- { "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type },
- { "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy.nodes },
- { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_LEGACY, NULL },
- { "Manager", "RuntimeWatchdogSec", config_parse_watchdog_sec, 0, &arg_runtime_watchdog },
- { "Manager", "RuntimeWatchdogPreSec", config_parse_watchdog_sec, 0, &arg_pretimeout_watchdog },
- { "Manager", "RebootWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog },
- { "Manager", "ShutdownWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog }, /* obsolete alias */
- { "Manager", "KExecWatchdogSec", config_parse_watchdog_sec, 0, &arg_kexec_watchdog },
- { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device },
- { "Manager", "RuntimeWatchdogPreGovernor", config_parse_string, CONFIG_PARSE_STRING_SAFE, &arg_watchdog_pretimeout_governor },
- { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
- { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs },
- { "Manager", "ProtectSystem", config_parse_protect_system_pid1, 0, &arg_protect_system },
+ { "Manager", "LogLevel", config_parse_level2, 0, NULL },
+ { "Manager", "LogTarget", config_parse_target, 0, NULL },
+ { "Manager", "LogColor", config_parse_color, 0, NULL },
+ { "Manager", "LogLocation", config_parse_location, 0, NULL },
+ { "Manager", "LogTime", config_parse_time, 0, NULL },
+ { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core },
+ { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, &arg_crash_chvt },
+ { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, &arg_crash_chvt },
+ { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell },
+ { "Manager", "CrashReboot", config_parse_crash_reboot, 0, &arg_crash_action },
+ { "Manager", "CrashAction", config_parse_crash_action, 0, &arg_crash_action },
+ { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status },
+ { "Manager", "StatusUnitFormat", config_parse_status_unit_format, 0, &arg_status_unit_format },
+ { "Manager", "CPUAffinity", config_parse_cpu_set, 0, &arg_cpu_affinity },
+ { "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type },
+ { "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy.nodes },
+ { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_LEGACY, NULL },
+ { "Manager", "RuntimeWatchdogSec", config_parse_watchdog_sec, 0, &arg_runtime_watchdog },
+ { "Manager", "RuntimeWatchdogPreSec", config_parse_watchdog_sec, 0, &arg_pretimeout_watchdog },
+ { "Manager", "RebootWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog },
+ { "Manager", "ShutdownWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog }, /* obsolete alias */
+ { "Manager", "KExecWatchdogSec", config_parse_watchdog_sec, 0, &arg_kexec_watchdog },
+ { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device },
+ { "Manager", "RuntimeWatchdogPreGovernor", config_parse_string, CONFIG_PARSE_STRING_SAFE, &arg_watchdog_pretimeout_governor },
+ { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
+ { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs },
+ { "Manager", "ProtectSystem", config_parse_protect_system_pid1, 0, &arg_protect_system },
#if HAVE_SECCOMP
- { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
+ { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
#else
- { "Manager", "SystemCallArchitectures", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
+ { "Manager", "SystemCallArchitectures", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
#endif
- { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
- { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_defaults.timer_accuracy_usec },
- { "Manager", "DefaultStandardOutput", config_parse_output_restricted, 0, &arg_defaults.std_output },
- { "Manager", "DefaultStandardError", config_parse_output_restricted, 0, &arg_defaults.std_error },
- { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_defaults.timeout_start_usec },
- { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_defaults.timeout_stop_usec },
- { "Manager", "DefaultTimeoutAbortSec", config_parse_default_timeout_abort, 0, NULL },
- { "Manager", "DefaultDeviceTimeoutSec", config_parse_sec, 0, &arg_defaults.device_timeout_usec },
- { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_defaults.restart_usec },
- { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_defaults.start_limit.interval }, /* obsolete alias */
- { "Manager", "DefaultStartLimitIntervalSec", config_parse_sec, 0, &arg_defaults.start_limit.interval },
- { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_defaults.start_limit.burst },
- { "Manager", "DefaultRestrictSUIDSGID", config_parse_bool, 0, &arg_defaults.restrict_suid_sgid },
- { "Manager", "DefaultEnvironment", config_parse_environ, arg_runtime_scope, &arg_default_environment },
- { "Manager", "ManagerEnvironment", config_parse_environ, arg_runtime_scope, &arg_manager_environment },
- { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_defaults.rlimit },
- { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_defaults.rlimit },
- { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_defaults.rlimit },
- { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_defaults.rlimit },
- { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_defaults.rlimit },
- { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_defaults.rlimit },
- { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_defaults.rlimit },
- { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_defaults.rlimit },
- { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_defaults.rlimit },
- { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_defaults.rlimit },
- { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_defaults.rlimit },
- { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_defaults.rlimit },
- { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_defaults.rlimit },
- { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_defaults.rlimit },
- { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_defaults.rlimit },
- { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_defaults.rlimit },
- { "Manager", "DefaultCPUAccounting", config_parse_warn_compat, DISABLED_LEGACY, NULL },
- { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_defaults.io_accounting },
- { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_defaults.ip_accounting },
- { "Manager", "DefaultBlockIOAccounting", config_parse_warn_compat, DISABLED_LEGACY, NULL },
- { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_defaults.memory_accounting },
- { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_defaults.tasks_accounting },
- { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_defaults.tasks_max },
- { "Manager", "DefaultMemoryPressureThresholdSec", config_parse_sec, 0, &arg_defaults.memory_pressure_threshold_usec },
- { "Manager", "DefaultMemoryPressureWatch", config_parse_memory_pressure_watch, 0, &arg_defaults.memory_pressure_watch },
- { "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, arg_runtime_scope, &arg_cad_burst_action },
- { "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_defaults.oom_policy },
- { "Manager", "DefaultOOMScoreAdjust", config_parse_oom_score_adjust, 0, NULL },
- { "Manager", "ReloadLimitIntervalSec", config_parse_sec, 0, &arg_reload_limit_interval_sec },
- { "Manager", "ReloadLimitBurst", config_parse_unsigned, 0, &arg_reload_limit_burst },
- { "Manager", "DefaultMemoryZSwapWriteback", config_parse_bool, 0, &arg_defaults.memory_zswap_writeback },
- { "Manager", "MinimumUptimeSec", config_parse_sec, 0, &arg_minimum_uptime_usec },
+ { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
+ { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_defaults.timer_accuracy_usec },
+ { "Manager", "DefaultStandardOutput", config_parse_output_restricted, 0, &arg_defaults.std_output },
+ { "Manager", "DefaultStandardError", config_parse_output_restricted, 0, &arg_defaults.std_error },
+ { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_defaults.timeout_start_usec },
+ { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_defaults.timeout_stop_usec },
+ { "Manager", "DefaultTimeoutAbortSec", config_parse_default_timeout_abort, 0, NULL },
+ { "Manager", "DefaultDeviceTimeoutSec", config_parse_sec, 0, &arg_defaults.device_timeout_usec },
+ { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_defaults.restart_usec },
+ { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_defaults.start_limit.interval }, /* obsolete alias */
+ { "Manager", "DefaultStartLimitIntervalSec", config_parse_sec, 0, &arg_defaults.start_limit.interval },
+ { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_defaults.start_limit.burst },
+ { "Manager", "DefaultRestrictSUIDSGID", config_parse_bool, 0, &arg_defaults.restrict_suid_sgid },
+ { "Manager", "DefaultEnvironment", config_parse_environ, arg_runtime_scope, &arg_default_environment },
+ { "Manager", "ManagerEnvironment", config_parse_environ, arg_runtime_scope, &arg_manager_environment },
+ { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_defaults.rlimit },
+ { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_defaults.rlimit },
+ { "Manager", "DefaultCPUAccounting", config_parse_warn_compat, DISABLED_LEGACY, NULL },
+ { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_defaults.io_accounting },
+ { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_defaults.ip_accounting },
+ { "Manager", "DefaultBlockIOAccounting", config_parse_warn_compat, DISABLED_LEGACY, NULL },
+ { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_defaults.memory_accounting },
+ { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_defaults.tasks_accounting },
+ { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_defaults.tasks_max },
+ { "Manager", "DefaultMemoryPressureThresholdSec", config_parse_sec, 0, &arg_defaults.pressure[PRESSURE_MEMORY].threshold_usec },
+ { "Manager", "DefaultMemoryPressureWatch", config_parse_pressure_watch, 0, &arg_defaults.pressure[PRESSURE_MEMORY].watch },
+ { "Manager", "DefaultCPUPressureThresholdSec", config_parse_sec, 0, &arg_defaults.pressure[PRESSURE_CPU].threshold_usec },
+ { "Manager", "DefaultCPUPressureWatch", config_parse_pressure_watch, 0, &arg_defaults.pressure[PRESSURE_CPU].watch },
+ { "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, arg_runtime_scope, &arg_cad_burst_action },
+ { "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_defaults.oom_policy },
+ { "Manager", "DefaultOOMScoreAdjust", config_parse_oom_score_adjust, 0, NULL },
+ { "Manager", "ReloadLimitIntervalSec", config_parse_sec, 0, &arg_reload_limit_interval_sec },
+ { "Manager", "ReloadLimitBurst", config_parse_unsigned, 0, &arg_reload_limit_burst },
+ { "Manager", "DefaultMemoryZSwapWriteback", config_parse_bool, 0, &arg_defaults.memory_zswap_writeback },
+ { "Manager", "MinimumUptimeSec", config_parse_sec, 0, &arg_minimum_uptime_usec },
#if ENABLE_SMACK
- { "Manager", "DefaultSmackProcessLabel", config_parse_string, 0, &arg_defaults.smack_process_label },
+ { "Manager", "DefaultSmackProcessLabel", config_parse_string, 0, &arg_defaults.smack_process_label },
#else
- { "Manager", "DefaultSmackProcessLabel", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
+ { "Manager", "DefaultSmackProcessLabel", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
#endif
{}
};
diff --git a/src/core/manager.c b/src/core/manager.c
index 85b68b86d2..c71d1a5d69 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -616,6 +616,8 @@ static char** sanitize_environment(char **l) {
l,
"CACHE_DIRECTORY",
"CONFIGURATION_DIRECTORY",
+ "CPU_PRESSURE_WATCH",
+ "CPU_PRESSURE_WRITE",
"CREDENTIALS_DIRECTORY",
"EXIT_CODE",
"EXIT_STATUS",
@@ -796,26 +798,37 @@ static int manager_setup_sigchld_event_source(Manager *m) {
return 0;
}
-int manager_setup_memory_pressure_event_source(Manager *m) {
+typedef int (*pressure_add_t)(sd_event *, sd_event_source **, sd_event_handler_t, void *);
+typedef int (*pressure_set_period_t)(sd_event_source *, usec_t, usec_t);
+
+static const struct {
+ pressure_add_t add;
+ pressure_set_period_t set_period;
+} pressure_dispatch_table[_PRESSURE_RESOURCE_MAX] = {
+ [PRESSURE_MEMORY] = { sd_event_add_memory_pressure, sd_event_source_set_memory_pressure_period },
+ [PRESSURE_CPU] = { sd_event_add_cpu_pressure, sd_event_source_set_cpu_pressure_period },
+};
+
+int manager_setup_pressure_event_source(Manager *m, PressureResource t) {
int r;
assert(m);
+ assert(t >= 0 && t < _PRESSURE_RESOURCE_MAX);
- m->memory_pressure_event_source = sd_event_source_disable_unref(m->memory_pressure_event_source);
+ m->pressure_event_source[t] = sd_event_source_disable_unref(m->pressure_event_source[t]);
- r = sd_event_add_memory_pressure(m->event, &m->memory_pressure_event_source, NULL, NULL);
+ r = pressure_dispatch_table[t].add(m->event, &m->pressure_event_source[t], NULL, NULL);
if (r < 0)
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r) || (r == -EHOSTDOWN) ? LOG_DEBUG : LOG_NOTICE, r,
- "Failed to establish memory pressure event source, ignoring: %m");
- else if (m->defaults.memory_pressure_threshold_usec != USEC_INFINITY) {
+ "Failed to establish %s pressure event source, ignoring: %m", pressure_resource_to_string(t));
+ else if (m->defaults.pressure[t].threshold_usec != USEC_INFINITY) {
- /* If there's a default memory pressure threshold set, also apply it to the service manager itself */
- r = sd_event_source_set_memory_pressure_period(
- m->memory_pressure_event_source,
- m->defaults.memory_pressure_threshold_usec,
- MEMORY_PRESSURE_DEFAULT_WINDOW_USEC);
+ r = pressure_dispatch_table[t].set_period(
+ m->pressure_event_source[t],
+ m->defaults.pressure[t].threshold_usec,
+ PRESSURE_DEFAULT_WINDOW_USEC);
if (r < 0)
- log_warning_errno(r, "Failed to adjust memory pressure threshold, ignoring: %m");
+ log_warning_errno(r, "Failed to adjust %s pressure threshold, ignoring: %m", pressure_resource_to_string(t));
}
return 0;
@@ -1001,9 +1014,11 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags,
if (r < 0)
return r;
- r = manager_setup_memory_pressure_event_source(m);
- if (r < 0)
- return r;
+ for (PressureResource t = 0; t < _PRESSURE_RESOURCE_MAX; t++) {
+ r = manager_setup_pressure_event_source(m, t);
+ if (r < 0)
+ return r;
+ }
#if HAVE_LIBBPF
if (MANAGER_IS_SYSTEM(m) && bpf_restrict_fs_supported(/* initialize= */ true)) {
@@ -1711,7 +1726,8 @@ Manager* manager_free(Manager *m) {
sd_event_source_unref(m->user_lookup_event_source);
sd_event_source_unref(m->handoff_timestamp_event_source);
sd_event_source_unref(m->pidref_event_source);
- sd_event_source_unref(m->memory_pressure_event_source);
+ FOREACH_ARRAY(pressure_event_source, m->pressure_event_source, _PRESSURE_RESOURCE_MAX)
+ sd_event_source_unref(*pressure_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
@@ -4300,8 +4316,7 @@ int manager_set_unit_defaults(Manager *m, const UnitDefaults *defaults) {
m->defaults.oom_score_adjust = defaults->oom_score_adjust;
m->defaults.oom_score_adjust_set = defaults->oom_score_adjust_set;
- m->defaults.memory_pressure_watch = defaults->memory_pressure_watch;
- m->defaults.memory_pressure_threshold_usec = defaults->memory_pressure_threshold_usec;
+ memcpy(m->defaults.pressure, defaults->pressure, sizeof(m->defaults.pressure));
m->defaults.memory_zswap_writeback = defaults->memory_zswap_writeback;
@@ -5195,8 +5210,10 @@ void unit_defaults_init(UnitDefaults *defaults, RuntimeScope scope) {
.tasks_max = DEFAULT_TASKS_MAX,
.timer_accuracy_usec = 1 * USEC_PER_MINUTE,
- .memory_pressure_watch = CGROUP_PRESSURE_WATCH_AUTO,
- .memory_pressure_threshold_usec = MEMORY_PRESSURE_DEFAULT_THRESHOLD_USEC,
+ .pressure = {
+ [PRESSURE_MEMORY] = { .watch = CGROUP_PRESSURE_WATCH_AUTO, .threshold_usec = PRESSURE_DEFAULT_THRESHOLD_USEC },
+ [PRESSURE_CPU] = { .watch = CGROUP_PRESSURE_WATCH_AUTO, .threshold_usec = PRESSURE_DEFAULT_THRESHOLD_USEC },
+ },
.oom_policy = OOM_STOP,
.oom_score_adjust_set = false,
diff --git a/src/core/manager.h b/src/core/manager.h
index 1c04deabee..7d58c330a1 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -149,8 +149,7 @@ typedef struct UnitDefaults {
bool memory_zswap_writeback;
- CGroupPressureWatch memory_pressure_watch;
- usec_t memory_pressure_threshold_usec;
+ CGroupPressure pressure[_PRESSURE_RESOURCE_MAX];
char *smack_process_label;
@@ -481,7 +480,7 @@ typedef struct Manager {
/* Dump*() are slow, so always rate limit them to 10 per 10 minutes */
RateLimit dump_ratelimit;
- sd_event_source *memory_pressure_event_source;
+ sd_event_source *pressure_event_source[_PRESSURE_RESOURCE_MAX];
/* For NFTSet= */
sd_netlink *nfnl;
@@ -562,7 +561,7 @@ void manager_unwatch_pidref(Manager *m, const PidRef *pid);
unsigned manager_dispatch_load_queue(Manager *m);
-int manager_setup_memory_pressure_event_source(Manager *m);
+int manager_setup_pressure_event_source(Manager *m, PressureResource t);
int manager_default_environment(Manager *m);
int manager_transient_environment_add(Manager *m, char **plus);
diff --git a/src/core/system.conf.in b/src/core/system.conf.in
index ef2c59bd79..d3cb0160a0 100644
--- a/src/core/system.conf.in
+++ b/src/core/system.conf.in
@@ -78,6 +78,8 @@
#DefaultLimitRTTIME=
#DefaultMemoryPressureThresholdSec=200ms
#DefaultMemoryPressureWatch=auto
+#DefaultCPUPressureThresholdSec=200ms
+#DefaultCPUPressureWatch=auto
#DefaultOOMPolicy=stop
#DefaultSmackProcessLabel=
#DefaultRestrictSUIDSGID=
diff --git a/src/core/unit.c b/src/core/unit.c
index 6dd5599f0a..dc205097e0 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -178,10 +178,9 @@ static void unit_init(Unit *u) {
if (u->type != UNIT_SLICE)
cc->tasks_max = u->manager->defaults.tasks_max;
- cc->memory_pressure_watch = u->manager->defaults.memory_pressure_watch;
- cc->memory_pressure_threshold_usec = u->manager->defaults.memory_pressure_threshold_usec;
-
cc->memory_zswap_writeback = u->manager->defaults.memory_zswap_writeback;
+
+ memcpy(cc->pressure, u->manager->defaults.pressure, sizeof(cc->pressure));
}
ec = unit_get_exec_context(u);
diff --git a/src/core/user.conf.in b/src/core/user.conf.in
index 9c37f4b54e..fe45c00b74 100644
--- a/src/core/user.conf.in
+++ b/src/core/user.conf.in
@@ -54,6 +54,8 @@
#DefaultLimitRTTIME=
#DefaultMemoryPressureThresholdSec=200ms
#DefaultMemoryPressureWatch=auto
+#DefaultCPUPressureThresholdSec=200ms
+#DefaultCPUPressureWatch=auto
#DefaultSmackProcessLabel=
#DefaultRestrictSUIDSGID=
#ReloadLimitIntervalSec=
diff --git a/src/core/varlink-cgroup.c b/src/core/varlink-cgroup.c
index 65c07ecfad..d4ec6049e6 100644
--- a/src/core/varlink-cgroup.c
+++ b/src/core/varlink-cgroup.c
@@ -323,8 +323,10 @@ int unit_cgroup_context_build_json(sd_json_variant **ret, const char *name, void
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("ManagedOOMMemoryPressureLimit", c->moom_mem_pressure_limit),
JSON_BUILD_PAIR_FINITE_USEC("ManagedOOMMemoryPressureDurationUSec", c->moom_mem_pressure_duration_usec),
SD_JSON_BUILD_PAIR_STRING("ManagedOOMPreference", managed_oom_preference_to_string(c->moom_preference)),
- SD_JSON_BUILD_PAIR_STRING("MemoryPressureWatch", cgroup_pressure_watch_to_string(c->memory_pressure_watch)),
- JSON_BUILD_PAIR_FINITE_USEC("MemoryPressureThresholdUSec", c->memory_pressure_threshold_usec),
+ SD_JSON_BUILD_PAIR_STRING("MemoryPressureWatch", cgroup_pressure_watch_to_string(c->pressure[PRESSURE_MEMORY].watch)),
+ JSON_BUILD_PAIR_FINITE_USEC("MemoryPressureThresholdUSec", c->pressure[PRESSURE_MEMORY].threshold_usec),
+ SD_JSON_BUILD_PAIR_STRING("CPUPressureWatch", cgroup_pressure_watch_to_string(c->pressure[PRESSURE_CPU].watch)),
+ JSON_BUILD_PAIR_FINITE_USEC("CPUPressureThresholdUSec", c->pressure[PRESSURE_CPU].threshold_usec),
/* Others */
SD_JSON_BUILD_PAIR_BOOLEAN("CoredumpReceive", c->coredump_receive));
diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c
index c039ea8e53..3953b8619f 100644
--- a/src/core/varlink-manager.c
+++ b/src/core/varlink-manager.c
@@ -106,8 +106,10 @@ static int manager_context_build_json(sd_json_variant **ret, const char *name, v
SD_JSON_BUILD_PAIR_BOOLEAN("DefaultTasksAccounting", m->defaults.tasks_accounting),
SD_JSON_BUILD_PAIR_CALLBACK("DefaultLimits", rlimit_table_build_json, m->defaults.rlimit),
SD_JSON_BUILD_PAIR_UNSIGNED("DefaultTasksMax", cgroup_tasks_max_resolve(&m->defaults.tasks_max)),
- JSON_BUILD_PAIR_FINITE_USEC("DefaultMemoryPressureThresholdUSec", m->defaults.memory_pressure_threshold_usec),
- SD_JSON_BUILD_PAIR_STRING("DefaultMemoryPressureWatch", cgroup_pressure_watch_to_string(m->defaults.memory_pressure_watch)),
+ JSON_BUILD_PAIR_FINITE_USEC("DefaultMemoryPressureThresholdUSec", m->defaults.pressure[PRESSURE_MEMORY].threshold_usec),
+ SD_JSON_BUILD_PAIR_STRING("DefaultMemoryPressureWatch", cgroup_pressure_watch_to_string(m->defaults.pressure[PRESSURE_MEMORY].watch)),
+ JSON_BUILD_PAIR_FINITE_USEC("DefaultCPUPressureThresholdUSec", m->defaults.pressure[PRESSURE_CPU].threshold_usec),
+ SD_JSON_BUILD_PAIR_STRING("DefaultCPUPressureWatch", cgroup_pressure_watch_to_string(m->defaults.pressure[PRESSURE_CPU].watch)),
JSON_BUILD_PAIR_FINITE_USEC("RuntimeWatchdogUSec", manager_get_watchdog(m, WATCHDOG_RUNTIME)),
JSON_BUILD_PAIR_FINITE_USEC("RebootWatchdogUSec", manager_get_watchdog(m, WATCHDOG_REBOOT)),
JSON_BUILD_PAIR_FINITE_USEC("KExecWatchdogUSec", manager_get_watchdog(m, WATCHDOG_KEXEC)),
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 4b539a35cf..aba6bf9b47 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -1949,8 +1949,7 @@ static int event_add_pressure(
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_origin_changed(e), -ECHILD);
- assert(resource >= 0 && resource < _PRESSURE_RESOURCE_MAX);
- const PressureResourceInfo *info = &pressure_resource_info[resource];
+ const PressureResourceInfo *info = pressure_resource_get_info(resource);
if (!callback)
callback = default_callback;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 84de3478a7..9c732543fa 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -2383,6 +2383,7 @@ static const BusProperty cgroup_properties[] = {
{ "ManagedOOMMemoryPressure", bus_append_string },
{ "ManagedOOMPreference", bus_append_string },
{ "MemoryPressureWatch", bus_append_string },
+ { "CPUPressureWatch", bus_append_string },
{ "DelegateSubgroup", bus_append_string },
{ "ManagedOOMMemoryPressureLimit", bus_append_parse_permyriad },
{ "MemoryAccounting", bus_append_parse_boolean },
@@ -2421,6 +2422,7 @@ static const BusProperty cgroup_properties[] = {
{ "SocketBindAllow", bus_append_socket_filter },
{ "SocketBindDeny", bus_append_socket_filter },
{ "MemoryPressureThresholdSec", bus_append_parse_sec_rename },
+ { "CPUPressureThresholdSec", bus_append_parse_sec_rename },
{ "NFTSet", bus_append_nft_set },
{ "BindNetworkInterface", bus_append_string },
diff --git a/src/shared/varlink-io.systemd.Manager.c b/src/shared/varlink-io.systemd.Manager.c
index ddf15b173e..f947c0a056 100644
--- a/src/shared/varlink-io.systemd.Manager.c
+++ b/src/shared/varlink-io.systemd.Manager.c
@@ -64,6 +64,10 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(DefaultMemoryPressureThresholdUSec, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd-system.conf.html#DefaultMemoryPressureWatch="),
SD_VARLINK_DEFINE_FIELD(DefaultMemoryPressureWatch, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd-system.conf.html#DefaultCPUPressureThresholdUSec="),
+ SD_VARLINK_DEFINE_FIELD(DefaultCPUPressureThresholdUSec, SD_VARLINK_INT, 0),
+ SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd-system.conf.html#DefaultCPUPressureWatch="),
+ SD_VARLINK_DEFINE_FIELD(DefaultCPUPressureWatch, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd-system.conf.html#RuntimeWatchdogSec="),
SD_VARLINK_DEFINE_FIELD(RuntimeWatchdogUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd-system.conf.html#RebootWatchdogSec="),
diff --git a/src/shared/varlink-io.systemd.Unit.c b/src/shared/varlink-io.systemd.Unit.c
index 05676210ef..a230f29dab 100644
--- a/src/shared/varlink-io.systemd.Unit.c
+++ b/src/shared/varlink-io.systemd.Unit.c
@@ -228,6 +228,10 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(MemoryPressureWatch, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#MemoryPressureThresholdSec="),
SD_VARLINK_DEFINE_FIELD(MemoryPressureThresholdUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#CPUPressureWatch="),
+ SD_VARLINK_DEFINE_FIELD(CPUPressureWatch, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man/"PROJECT_VERSION_STR"/systemd.resource-control.html#CPUPressureThresholdSec="),
+ SD_VARLINK_DEFINE_FIELD(CPUPressureThresholdUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
/* Others */
SD_VARLINK_FIELD_COMMENT("Reflects whether to forward coredumps for processes that crash within this cgroup"),
diff --git a/test/integration-tests/TEST-79-MEMPRESS/meson.build b/test/integration-tests/TEST-79-PRESSURE/meson.build
similarity index 100%
rename from test/integration-tests/TEST-79-MEMPRESS/meson.build
rename to test/integration-tests/TEST-79-PRESSURE/meson.build
diff --git a/test/integration-tests/meson.build b/test/integration-tests/meson.build
index 5d71e87c79..198371ccae 100644
--- a/test/integration-tests/meson.build
+++ b/test/integration-tests/meson.build
@@ -91,7 +91,7 @@ foreach dirname : [
'TEST-74-AUX-UTILS',
'TEST-75-RESOLVED',
'TEST-78-SIGQUEUE',
- 'TEST-79-MEMPRESS',
+ 'TEST-79-PRESSURE',
'TEST-80-NOTIFYACCESS',
'TEST-81-GENERATORS',
'TEST-82-SOFTREBOOT',
diff --git a/test/units/TEST-79-MEMPRESS.sh b/test/units/TEST-79-PRESSURE.sh
similarity index 56%
rename from test/units/TEST-79-MEMPRESS.sh
rename to test/units/TEST-79-PRESSURE.sh
index 0659160966..d4e4a9e06b 100755
--- a/test/units/TEST-79-MEMPRESS.sh
+++ b/test/units/TEST-79-PRESSURE.sh
@@ -13,7 +13,7 @@ if ! cat /proc/pressure/memory >/dev/null ; then
exit 0
fi
-CGROUP=/sys/fs/cgroup/"$(systemctl show TEST-79-MEMPRESS.service -P ControlGroup)"
+CGROUP=/sys/fs/cgroup/"$(systemctl show TEST-79-PRESSURE.service -P ControlGroup)"
test -d "$CGROUP"
if ! test -f "$CGROUP"/memory.pressure ; then
@@ -61,4 +61,57 @@ systemd-run \
rm "$SCRIPT"
+# Now test CPU pressure
+
+if ! cat /proc/pressure/cpu >/dev/null ; then
+ echo "kernel has no CPU PSI support." >&2
+ echo OK >/testok
+ exit 0
+fi
+
+if ! test -f "$CGROUP"/cpu.pressure ; then
+ echo "No CPU accounting/PSI delegated via cgroup, can't test." >&2
+ echo OK >/testok
+ exit 0
+fi
+
+UNIT="test-cpupress-$RANDOM.service"
+SCRIPT="/tmp/cpupress-$RANDOM.sh"
+
+cat >"$SCRIPT" <<'EOF'
+#!/usr/bin/env bash
+
+set -ex
+
+export
+id
+
+test -n "$CPU_PRESSURE_WATCH"
+test "$CPU_PRESSURE_WATCH" != /dev/null
+test -w "$CPU_PRESSURE_WATCH"
+
+ls -al "$CPU_PRESSURE_WATCH"
+
+EXPECTED="$(echo -n -e "some 123000 2000000\x00" | base64)"
+
+test "$EXPECTED" = "$CPU_PRESSURE_WRITE"
+
+EOF
+
+chmod +x "$SCRIPT"
+
+systemd-run \
+ -u "$UNIT" \
+ -p Type=exec \
+ -p ProtectControlGroups=1 \
+ -p DynamicUser=1 \
+ -p CPUPressureWatch=on \
+ -p CPUPressureThresholdSec=123ms \
+ -p BindPaths=$SCRIPT \
+ `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \
+ -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+ --wait "$SCRIPT"
+
+rm "$SCRIPT"
+
touch /testok