]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-mempress: Support unprivileged operation
authorDaan De Meyer <daan@amutable.com>
Sat, 7 Mar 2026 20:25:09 +0000 (21:25 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 9 Apr 2026 20:47:10 +0000 (22:47 +0200)
man/rules/meson.build
man/sd_event_add_memory_pressure.xml
src/basic/psi-util.c
src/basic/psi-util.h
src/core/exec-invoke.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-event/event-source.h
src/libsystemd/sd-event/sd-event.c
src/systemd/sd-event.h
src/test/meson.build
src/test/test-pressure.c [moved from src/test/test-mempress.c with 58% similarity]

index aa2653ce0d82e2e28a063dc016bff7e64b56e8f4..81e7ef4f882620cf2c9f7c8f218bf14e0e910e08 100644 (file)
@@ -608,7 +608,10 @@ manpages = [
   ''],
  ['sd_event_add_memory_pressure',
   '3',
-  ['sd_event_source_set_memory_pressure_period',
+  ['sd_event_add_cpu_pressure',
+   'sd_event_source_set_cpu_pressure_period',
+   'sd_event_source_set_cpu_pressure_type',
+   'sd_event_source_set_memory_pressure_period',
    'sd_event_source_set_memory_pressure_type',
    'sd_event_trim_memory'],
   ''],
index b112855f061b04056d50626c58cec60c238ef6b3..1e6b734738f6c862b86e271b67e24ae82607e17f 100644 (file)
     <refname>sd_event_source_set_memory_pressure_period</refname>
     <refname>sd_event_trim_memory</refname>
 
-    <refpurpose>Add and configure an event source run as result of memory pressure</refpurpose>
+    <refname>sd_event_add_cpu_pressure</refname>
+    <refname>sd_event_source_set_cpu_pressure_type</refname>
+    <refname>sd_event_source_set_cpu_pressure_period</refname>
+
+    <refpurpose>Add and configure an event source run as result of memory or CPU pressure</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
         <paramdef>uint64_t <parameter>window_usec</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_event_add_cpu_pressure</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+        <paramdef>sd_event_source **<parameter>ret_source</parameter></paramdef>
+        <paramdef>sd_event_handler_t <parameter>handler</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_cpu_pressure_type</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>const char *<parameter>type</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_cpu_pressure_period</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>uint64_t <parameter>threshold_usec</parameter></paramdef>
+        <paramdef>uint64_t <parameter>window_usec</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_event_trim_memory</function></funcdef>
         <paramdef>void</paramdef>
     <title>Description</title>
 
     <para><function>sd_event_add_memory_pressure()</function> adds a new event source that is triggered
-    whenever memory pressure is seen. This functionality is built around the Linux kernel's <ulink
+    whenever memory pressure is seen. Similarly,
+    <function>sd_event_add_cpu_pressure()</function> adds a new event source that is triggered whenever CPU
+    pressure is seen. This functionality is built around the Linux kernel's <ulink
     url="https://docs.kernel.org/accounting/psi.html">Pressure Stall Information (PSI)</ulink> logic.</para>
 
-    <para>Expects an event loop object as first parameter, and returns the allocated event source object in
-    the second parameter, on success. The <parameter>handler</parameter> parameter is a function to call when
-    memory pressure is seen, or <constant>NULL</constant>. The handler function will be passed the
+    <para>Both functions expect an event loop object as first parameter, and return the allocated event source
+    object in the second parameter, on success. The <parameter>handler</parameter> parameter is a function to
+    call when pressure is seen, or <constant>NULL</constant>. The handler function will be passed the
     <parameter>userdata</parameter> pointer, which may be chosen freely by the caller. The handler may return
     negative to signal an error (see below), other return values are ignored. If
     <parameter>handler</parameter> is <constant>NULL</constant>, a default handler that compacts allocation
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     with <constant>SD_EVENT_OFF</constant>.</para>
 
-    <para>If the second parameter of <function>sd_event_add_memory_pressure()</function> is
+    <para>If the second parameter of <function>sd_event_add_memory_pressure()</function> or
+    <function>sd_event_add_cpu_pressure()</function> is
     <constant>NULL</constant> no reference to the event source object is returned. In this case, the event
     source is considered "floating", and will be destroyed implicitly when the event loop itself is
     destroyed.</para>
 
-    <para>The event source will fire according to the following logic:</para>
+    <para>The memory pressure event source will fire according to the following logic:</para>
 
     <orderedlist>
       <listitem><para>If the
       <filename>/proc/pressure/memory</filename> is watched instead.</para></listitem>
     </orderedlist>
 
+    <para>The CPU pressure event source follows the same logic, but uses the
+    <varname>$CPU_PRESSURE_WATCH</varname>/<varname>$CPU_PRESSURE_WRITE</varname> environment variables,
+    the <filename>cpu.pressure</filename> cgroup file, and the system-wide PSI interface file
+    <filename>/proc/pressure/cpu</filename> instead. Note that <filename>/proc/pressure/cpu</filename> only
+    provides the <literal>some</literal> line, not the <literal>full</literal> line, so only
+    <literal>some</literal> is valid when watching at the system level.</para>
+
     <para>Or in other words: preferably any explicit configuration passed in by an invoking service manager
     (or similar) is used as notification source, before falling back to local notifications of the service,
     and finally to global notifications of the system.</para>
 
     <para>The <function>sd_event_source_set_memory_pressure_type()</function> and
     <function>sd_event_source_set_memory_pressure_period()</function> functions can be used to fine-tune the
-    PSI parameters for pressure notifications. The former takes either <literal>some</literal>,
+    PSI parameters for memory pressure notifications. The former takes either <literal>some</literal>,
     <literal>full</literal> as second parameter, the latter takes threshold and period times in microseconds
     as parameters. For details about these three parameters see the PSI documentation. Note that these two
     calls must be invoked immediately after allocating the event source, as they must be configured before
     environment variables (or in other words: configuration supplied by a service manager wins over internal
     settings).</para>
 
+    <para>Similarly, <function>sd_event_source_set_cpu_pressure_type()</function> and
+    <function>sd_event_source_set_cpu_pressure_period()</function> can be used to fine-tune the PSI
+    parameters for CPU pressure notifications. They work identically to their memory pressure counterparts.
+    The type parameter takes either <literal>some</literal> or <literal>full</literal>, and the period
+    function takes threshold and period times in microseconds. The same constraints apply: these calls must
+    be invoked immediately after allocating the event source, and will fail if CPU pressure parameterization
+    has been passed in via the
+    <varname>$CPU_PRESSURE_WATCH</varname>/<varname>$CPU_PRESSURE_WRITE</varname> environment
+    variables.</para>
+
     <para>The <function>sd_event_trim_memory()</function> function releases various internal allocation
     caches maintained by <filename>libsystemd</filename> and then invokes glibc's <citerefentry
     project='man-pages'><refentrytitle>malloc_trim</refentrytitle><manvolnum>3</manvolnum></citerefentry>. This
         <varlistentry>
           <term><constant>-EHOSTDOWN</constant></term>
 
-          <listitem><para>The <varname>$MEMORY_PRESSURE_WATCH</varname> variable has been set to the literal
-          string <filename>/dev/null</filename>, in order to explicitly disable memory pressure
+          <listitem><para>The <varname>$MEMORY_PRESSURE_WATCH</varname> or
+          <varname>$CPU_PRESSURE_WATCH</varname> variable has been set to the literal
+          string <filename>/dev/null</filename>, in order to explicitly disable pressure
           handling.</para>
 
           <xi:include href="version-info.xml" xpointer="v254"/></listitem>
         <varlistentry>
           <term><constant>-EBADMSG</constant></term>
 
-          <listitem><para>The <varname>$MEMORY_PRESSURE_WATCH</varname> variable has been set to an invalid
+          <listitem><para>The <varname>$MEMORY_PRESSURE_WATCH</varname> or
+          <varname>$CPU_PRESSURE_WATCH</varname> variable has been set to an invalid
           string, for example a relative rather than an absolute path.</para>
 
           <xi:include href="version-info.xml" xpointer="v254"/></listitem>
         <varlistentry>
           <term><constant>-ENOTTY</constant></term>
 
-          <listitem><para>The <varname>$MEMORY_PRESSURE_WATCH</varname> variable points to a regular file
+          <listitem><para>The <varname>$MEMORY_PRESSURE_WATCH</varname> or
+          <varname>$CPU_PRESSURE_WATCH</varname> variable points to a regular file
           outside of the procfs or cgroupfs file systems.</para>
 
           <xi:include href="version-info.xml" xpointer="v254"/></listitem>
         <varlistentry>
           <term><constant>-EOPNOTSUPP</constant></term>
 
-          <listitem><para>No configuration via <varname>$MEMORY_PRESSURE_WATCH</varname> has been specified
-          and the local kernel does not support the PSI interface.</para>
+          <listitem><para>No configuration via <varname>$MEMORY_PRESSURE_WATCH</varname> or
+          <varname>$CPU_PRESSURE_WATCH</varname> has been specified and the local kernel does not support the
+          PSI interface.</para>
 
           <xi:include href="version-info.xml" xpointer="v254"/></listitem>
         </varlistentry>
         <varlistentry>
           <term><constant>-EBUSY</constant></term>
 
-          <listitem><para>This is returned by <function>sd_event_source_set_memory_pressure_type()</function>
-          and <function>sd_event_source_set_memory_pressure_period()</function> if invoked on event sources
+          <listitem><para>This is returned by <function>sd_event_source_set_memory_pressure_type()</function>,
+          <function>sd_event_source_set_memory_pressure_period()</function>,
+          <function>sd_event_source_set_cpu_pressure_type()</function>,
+          and <function>sd_event_source_set_cpu_pressure_period()</function> if invoked on event sources
           at a time later than immediately after allocating them.</para>
 
           <xi:include href="version-info.xml" xpointer="v254"/></listitem>
     <function>sd_event_source_set_memory_pressure_type()</function>,
     <function>sd_event_source_set_memory_pressure_period()</function>, and
     <function>sd_event_trim_memory()</function> were added in version 254.</para>
+    <para><function>sd_event_add_cpu_pressure()</function>,
+    <function>sd_event_source_set_cpu_pressure_type()</function>, and
+    <function>sd_event_source_set_cpu_pressure_period()</function> were added in version 261.</para>
   </refsect1>
 
   <refsect1>
index df1ccbc1b20fb619e6fe6def58096937b676657d..cf05485dc7b674dbf34c0050882b9c19cc92f368 100644 (file)
@@ -10,6 +10,7 @@
 #include "fileio.h"
 #include "parse-util.h"
 #include "psi-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 
@@ -104,6 +105,26 @@ int read_resource_pressure(const char *path, PressureType type, ResourcePressure
         return 0;
 }
 
+const PressureResourceInfo pressure_resource_info[_PRESSURE_RESOURCE_MAX] = {
+        [PRESSURE_MEMORY] = {
+                .name      = "memory",
+                .env_watch = "MEMORY_PRESSURE_WATCH",
+                .env_write = "MEMORY_PRESSURE_WRITE",
+        },
+        [PRESSURE_CPU] = {
+                .name      = "cpu",
+                .env_watch = "CPU_PRESSURE_WATCH",
+                .env_write = "CPU_PRESSURE_WRITE",
+        },
+};
+
+static const char* const pressure_resource_table[_PRESSURE_RESOURCE_MAX] = {
+        [PRESSURE_MEMORY] = "memory",
+        [PRESSURE_CPU]    = "cpu",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(pressure_resource, PressureResource);
+
 int is_pressure_supported(void) {
         static thread_local int cached = -1;
         int r;
index f5e79960a81590448d215d0283a57b17d252768b..aed74ef742d5a72c36120b8267b5b96537ce2a29 100644 (file)
@@ -9,6 +9,13 @@ typedef enum PressureType {
         PRESSURE_TYPE_FULL,
 } PressureType;
 
+typedef enum PressureResource {
+        PRESSURE_MEMORY,
+        PRESSURE_CPU,
+        _PRESSURE_RESOURCE_MAX,
+        _PRESSURE_RESOURCE_INVALID = -EINVAL,
+} PressureResource;
+
 /* Averages are stored in fixed-point with 11 bit fractions */
 typedef struct ResourcePressure {
         loadavg_t avg10;
@@ -27,7 +34,18 @@ int read_resource_pressure(const char *path, PressureType type, ResourcePressure
 /* Was the kernel compiled with CONFIG_PSI=y? 1 if yes, 0 if not, negative on error. */
 int is_pressure_supported(void);
 
-/* Default parameters for memory pressure watch logic in sd-event and PID 1 */
-#define MEMORY_PRESSURE_DEFAULT_TYPE "some"
-#define MEMORY_PRESSURE_DEFAULT_THRESHOLD_USEC (200 * USEC_PER_MSEC)
-#define MEMORY_PRESSURE_DEFAULT_WINDOW_USEC (2 * USEC_PER_SEC)
+/* Metadata for each pressure resource type, for use in sd-event and PID 1 */
+typedef struct PressureResourceInfo {
+        const char *name;       /* "memory", "cpu", "io" */
+        const char *env_watch;  /* "MEMORY_PRESSURE_WATCH", etc. */
+        const char *env_write;  /* "MEMORY_PRESSURE_WRITE", etc. */
+} PressureResourceInfo;
+
+extern const PressureResourceInfo pressure_resource_info[_PRESSURE_RESOURCE_MAX];
+
+DECLARE_STRING_TABLE_LOOKUP(pressure_resource, PressureResource);
+
+/* Default parameters for pressure watch logic in sd-event and PID 1 */
+#define PRESSURE_DEFAULT_TYPE "some"
+#define PRESSURE_DEFAULT_THRESHOLD_USEC (200 * USEC_PER_MSEC)
+#define PRESSURE_DEFAULT_WINDOW_USEC (2 * USEC_PER_SEC)
index b91a964cdd6abdc1acb037c49be62aebab8bdd24..7500888c414a3814a972d08de38d301779495e70 100644 (file)
@@ -2224,10 +2224,10 @@ static int build_environment(
                         _cleanup_free_ char *b = NULL, *x = NULL;
 
                         if (asprintf(&b, "%s " USEC_FMT " " USEC_FMT,
-                                     MEMORY_PRESSURE_DEFAULT_TYPE,
-                                     cgroup_context->memory_pressure_threshold_usec == USEC_INFINITY ? MEMORY_PRESSURE_DEFAULT_THRESHOLD_USEC :
-                                     CLAMP(cgroup_context->memory_pressure_threshold_usec, 1U, MEMORY_PRESSURE_DEFAULT_WINDOW_USEC),
-                                     MEMORY_PRESSURE_DEFAULT_WINDOW_USEC) < 0)
+                                     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),
+                                     PRESSURE_DEFAULT_WINDOW_USEC) < 0)
                                 return -ENOMEM;
 
                         if (base64mem(b, strlen(b) + 1, &x) < 0)
index 619bcf820c8755c71ce1df274b1f270a1bb1164d..5f5eca60833b207ecf5632dfbce04752feb05988 100644 (file)
@@ -1096,4 +1096,7 @@ global:
         sd_varlink_call_and_upgrade;
         sd_varlink_reply_and_upgrade;
         sd_varlink_set_sentinel;
+        sd_event_add_cpu_pressure;
+        sd_event_source_set_cpu_pressure_type;
+        sd_event_source_set_cpu_pressure_period;
 } LIBSYSTEMD_260;
index e4dc456fae8ea1fbbbf10586a4b886da73629729..c7d5ba166da31fea2d68281d1cf800e74ecefa18 100644 (file)
@@ -26,6 +26,7 @@ typedef enum EventSourceType {
         SOURCE_WATCHDOG,
         SOURCE_INOTIFY,
         SOURCE_MEMORY_PRESSURE,
+        SOURCE_CPU_PRESSURE,
         _SOURCE_EVENT_SOURCE_TYPE_MAX,
         _SOURCE_EVENT_SOURCE_TYPE_INVALID = -EINVAL,
 } EventSourceType;
@@ -144,7 +145,7 @@ struct sd_event_source {
                         size_t write_buffer_size;
                         uint32_t events, revents;
                         LIST_FIELDS(sd_event_source, write_list);
-                } memory_pressure;
+                } pressure;
         };
 };
 
index 19feff5668852dca46d91b503087ae788369fc0a..4b539a35cf60ba4a628f0d8d0f483c4dba902090 100644 (file)
@@ -76,6 +76,7 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX]
         [SOURCE_WATCHDOG]            = "watchdog",
         [SOURCE_INOTIFY]             = "inotify",
         [SOURCE_MEMORY_PRESSURE]     = "memory-pressure",
+        [SOURCE_CPU_PRESSURE]        = "cpu-pressure",
 };
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
@@ -99,7 +100,8 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
                SOURCE_SIGNAL,                   \
                SOURCE_DEFER,                    \
                SOURCE_INOTIFY,                  \
-               SOURCE_MEMORY_PRESSURE)
+               SOURCE_MEMORY_PRESSURE,          \
+               SOURCE_CPU_PRESSURE)
 
 /* This is used to assert that we didn't pass an unexpected source type to event_source_time_prioq_put().
  * Time sources and ratelimited sources can be passed, so effectively this is the same as the
@@ -144,8 +146,8 @@ struct sd_event {
         /* A list of inotify objects that already have events buffered which aren't processed yet */
         LIST_HEAD(InotifyData, buffered_inotify_data_list);
 
-        /* A list of memory pressure event sources that still need their subscription string written */
-        LIST_HEAD(sd_event_source, memory_pressure_write_list);
+        /* A list of pressure event sources that still need their subscription string written */
+        LIST_HEAD(sd_event_source, pressure_write_list);
 
         uint64_t origin_id;
 
@@ -564,63 +566,65 @@ static int source_child_pidfd_register(sd_event_source *s, int enabled) {
         return 0;
 }
 
-static void source_memory_pressure_unregister(sd_event_source *s) {
+#define EVENT_SOURCE_IS_PRESSURE(s) IN_SET((s)->type, SOURCE_MEMORY_PRESSURE, SOURCE_CPU_PRESSURE)
+
+static void source_pressure_unregister(sd_event_source *s) {
         assert(s);
-        assert(s->type == SOURCE_MEMORY_PRESSURE);
+        assert(EVENT_SOURCE_IS_PRESSURE(s));
 
         if (event_origin_changed(s->event))
                 return;
 
-        if (!s->memory_pressure.registered)
+        if (!s->pressure.registered)
                 return;
 
-        if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->memory_pressure.fd, NULL) < 0)
+        if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->pressure.fd, NULL) < 0)
                 log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll, ignoring: %m",
                                 strna(s->description), event_source_type_to_string(s->type));
 
-        s->memory_pressure.registered = false;
+        s->pressure.registered = false;
 }
 
-static int source_memory_pressure_register(sd_event_source *s, int enabled) {
+static int source_pressure_register(sd_event_source *s, int enabled) {
         assert(s);
-        assert(s->type == SOURCE_MEMORY_PRESSURE);
+        assert(EVENT_SOURCE_IS_PRESSURE(s));
         assert(enabled != SD_EVENT_OFF);
 
         struct epoll_event ev = {
-                .events = s->memory_pressure.write_buffer_size > 0 ? EPOLLOUT :
-                          (s->memory_pressure.events | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0)),
+                .events = s->pressure.write_buffer_size > 0 ? EPOLLOUT :
+                          (s->pressure.events | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0)),
                 .data.ptr = s,
         };
 
         if (epoll_ctl(s->event->epoll_fd,
-                      s->memory_pressure.registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD,
-                      s->memory_pressure.fd, &ev) < 0)
+                      s->pressure.registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD,
+                      s->pressure.fd, &ev) < 0)
                 return -errno;
 
-        s->memory_pressure.registered = true;
+        s->pressure.registered = true;
         return 0;
 }
 
-static void source_memory_pressure_add_to_write_list(sd_event_source *s) {
+static void source_pressure_add_to_write_list(sd_event_source *s) {
         assert(s);
-        assert(s->type == SOURCE_MEMORY_PRESSURE);
+        assert(EVENT_SOURCE_IS_PRESSURE(s));
 
-        if (s->memory_pressure.in_write_list)
+        if (s->pressure.in_write_list)
                 return;
 
-        LIST_PREPEND(memory_pressure.write_list, s->event->memory_pressure_write_list, s);
-        s->memory_pressure.in_write_list = true;
+        LIST_PREPEND(pressure.write_list, s->event->pressure_write_list, s);
+        s->pressure.in_write_list = true;
 }
 
-static void source_memory_pressure_remove_from_write_list(sd_event_source *s) {
+static void source_pressure_remove_from_write_list(sd_event_source *s) {
         assert(s);
-        assert(s->type == SOURCE_MEMORY_PRESSURE);
+        assert(EVENT_SOURCE_IS_PRESSURE(s));
 
-        if (!s->memory_pressure.in_write_list)
+        if (!s->pressure.in_write_list)
                 return;
 
-        LIST_REMOVE(memory_pressure.write_list, s->event->memory_pressure_write_list, s);
-        s->memory_pressure.in_write_list = false;
+        LIST_REMOVE(pressure.write_list, s->event->pressure_write_list, s);
+        s->pressure.in_write_list = false;
 }
 
 static clockid_t event_source_type_to_clock(EventSourceType t) {
@@ -1047,8 +1051,9 @@ static void source_disconnect(sd_event_source *s) {
         }
 
         case SOURCE_MEMORY_PRESSURE:
-                source_memory_pressure_remove_from_write_list(s);
-                source_memory_pressure_unregister(s);
+        case SOURCE_CPU_PRESSURE:
+                source_pressure_remove_from_write_list(s);
+                source_pressure_unregister(s);
                 break;
 
         default:
@@ -1111,9 +1116,9 @@ static sd_event_source* source_free(sd_event_source *s) {
                         s->child.pidfd = safe_close(s->child.pidfd);
         }
 
-        if (s->type == SOURCE_MEMORY_PRESSURE) {
-                s->memory_pressure.fd = safe_close(s->memory_pressure.fd);
-                s->memory_pressure.write_buffer = mfree(s->memory_pressure.write_buffer);
+        if (EVENT_SOURCE_IS_PRESSURE(s)) {
+                s->pressure.fd = safe_close(s->pressure.fd);
+                s->pressure.write_buffer = mfree(s->pressure.write_buffer);
         }
 
         if (s->destroy_callback)
@@ -1191,7 +1196,8 @@ static sd_event_source* source_new(sd_event *e, bool floating, EventSourceType t
                 [SOURCE_POST]                = endoffsetof_field(sd_event_source, post),
                 [SOURCE_EXIT]                = endoffsetof_field(sd_event_source, exit),
                 [SOURCE_INOTIFY]             = endoffsetof_field(sd_event_source, inotify),
-                [SOURCE_MEMORY_PRESSURE]     = endoffsetof_field(sd_event_source, memory_pressure),
+                [SOURCE_MEMORY_PRESSURE]     = endoffsetof_field(sd_event_source, pressure),
+                [SOURCE_CPU_PRESSURE]        = endoffsetof_field(sd_event_source, pressure),
         };
 
         sd_event_source *s;
@@ -1917,17 +1923,21 @@ static int memory_pressure_callback(sd_event_source *s, void *userdata) {
         return 0;
 }
 
-_public_ int sd_event_add_memory_pressure(
+static int event_add_pressure(
                 sd_event *e,
                 sd_event_source **ret,
                 sd_event_handler_t callback,
-                void *userdata) {
+                void *userdata,
+                EventSourceType type,
+                sd_event_handler_t default_callback,
+                PressureResource resource) {
 
         _cleanup_free_ char *w = NULL;
         _cleanup_(source_freep) sd_event_source *s = NULL;
         _cleanup_close_ int path_fd = -EBADF, fd = -EBADF;
         _cleanup_free_ void *write_buffer = NULL;
-        const char *watch, *watch_fallback = NULL, *env;
+        _cleanup_free_ char *watch_fallback = NULL;
+        const char *watch, *env;
         size_t write_buffer_size = 0;
         struct stat st;
         uint32_t events;
@@ -1939,32 +1949,35 @@ _public_ int sd_event_add_memory_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];
+
         if (!callback)
-                callback = memory_pressure_callback;
+                callback = default_callback;
 
-        s = source_new(e, !ret, SOURCE_MEMORY_PRESSURE);
+        s = source_new(e, !ret, type);
         if (!s)
                 return -ENOMEM;
 
         s->wakeup = WAKEUP_EVENT_SOURCE;
-        s->memory_pressure.callback = callback;
+        s->pressure.callback = callback;
         s->userdata = userdata;
         s->enabled = SD_EVENT_ON;
-        s->memory_pressure.fd = -EBADF;
+        s->pressure.fd = -EBADF;
 
-        env = secure_getenv("MEMORY_PRESSURE_WATCH");
+        env = secure_getenv(info->env_watch);
         if (env) {
                 if (isempty(env) || path_equal(env, "/dev/null"))
                         return log_debug_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
-                                               "Memory pressure logic is explicitly disabled via $MEMORY_PRESSURE_WATCH.");
+                                               "Pressure logic is explicitly disabled via $%s.", info->env_watch);
 
                 if (!path_is_absolute(env) || !path_is_normalized(env))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                               "$MEMORY_PRESSURE_WATCH set to invalid path: %s", env);
+                                               "$%s set to invalid path: %s", info->env_watch, env);
 
                 watch = env;
 
-                env = secure_getenv("MEMORY_PRESSURE_WRITE");
+                env = secure_getenv(info->env_write);
                 if (env) {
                         r = unbase64mem(env, &write_buffer, &write_buffer_size);
                         if (r < 0)
@@ -1980,8 +1993,8 @@ _public_ int sd_event_add_memory_pressure(
                 if (r == 0)
                         return -EOPNOTSUPP;
 
-                /* By default we want to watch memory pressure on the local cgroup, but we'll fall back on
-                 * the system wide pressure if for some reason we cannot (which could be: memory controller
+                /* By default we want to watch pressure on the local cgroup, but we'll fall back on
+                 * the system wide pressure if for some reason we cannot (which could be: controller
                  * not delegated to us, or PSI simply not available in the kernel). */
 
                 _cleanup_free_ char *cg = NULL;
@@ -1989,12 +2002,19 @@ _public_ int sd_event_add_memory_pressure(
                 if (r < 0)
                         return r;
 
-                w = path_join("/sys/fs/cgroup", cg, "memory.pressure");
+                _cleanup_free_ char *cgroup_file = strjoin(info->name, ".pressure");
+                if (!cgroup_file)
+                        return -ENOMEM;
+
+                w = path_join("/sys/fs/cgroup", cg, cgroup_file);
                 if (!w)
                         return -ENOMEM;
 
                 watch = w;
-                watch_fallback = "/proc/pressure/memory";
+
+                watch_fallback = strjoin("/proc/pressure/", info->name);
+                if (!watch_fallback)
+                        return -ENOMEM;
 
                 /* Android uses three levels in its userspace low memory killer logic:
                  *     some  70000 1000000
@@ -2011,9 +2031,9 @@ _public_ int sd_event_add_memory_pressure(
                  * kernel will allow us to do unprivileged, also in the future. */
                 if (asprintf((char**) &write_buffer,
                              "%s " USEC_FMT " " USEC_FMT,
-                             MEMORY_PRESSURE_DEFAULT_TYPE,
-                             MEMORY_PRESSURE_DEFAULT_THRESHOLD_USEC,
-                             MEMORY_PRESSURE_DEFAULT_WINDOW_USEC) < 0)
+                             PRESSURE_DEFAULT_TYPE,
+                             PRESSURE_DEFAULT_THRESHOLD_USEC,
+                             PRESSURE_DEFAULT_WINDOW_USEC) < 0)
                         return -ENOMEM;
 
                 write_buffer_size = strlen(write_buffer) + 1;
@@ -2080,24 +2100,24 @@ _public_ int sd_event_add_memory_pressure(
         else
                 return -EBADF;
 
-        s->memory_pressure.fd = TAKE_FD(fd);
-        s->memory_pressure.write_buffer = TAKE_PTR(write_buffer);
-        s->memory_pressure.write_buffer_size = write_buffer_size;
-        s->memory_pressure.events = events;
-        s->memory_pressure.locked = locked;
+        s->pressure.fd = TAKE_FD(fd);
+        s->pressure.write_buffer = TAKE_PTR(write_buffer);
+        s->pressure.write_buffer_size = write_buffer_size;
+        s->pressure.events = events;
+        s->pressure.locked = locked;
 
         /* So here's the thing: if we are talking to PSI we need to write the watch string before adding the
          * fd to epoll (if we ignore this, then the watch won't work). Hence we'll not actually register the
-         * fd with the epoll right-away. Instead, we just add the event source to a list of memory pressure
-         * event sources on which writes must be executed before the first event loop iteration is
-         * executed. (We could also write the data here, right away, but we want to give the caller the
-         * freedom to call sd_event_source_set_memory_pressure_type() and
-         * sd_event_source_set_memory_pressure_rate() before we write it. */
-
-        if (s->memory_pressure.write_buffer_size > 0)
-                source_memory_pressure_add_to_write_list(s);
+         * fd with the epoll right-away. Instead, we just add the event source to a list of pressure event
+         * sources on which writes must be executed before the first event loop iteration is executed. (We
+         * could also write the data here, right away, but we want to give the caller the freedom to call
+         * sd_event_source_set_{memory,cpu}_pressure_type() and
+         * sd_event_source_set_{memory,cpu}_pressure_period() before we write it. */
+
+        if (s->pressure.write_buffer_size > 0)
+                source_pressure_add_to_write_list(s);
         else {
-                r = source_memory_pressure_register(s, s->enabled);
+                r = source_pressure_register(s, s->enabled);
                 if (r < 0)
                         return r;
         }
@@ -2109,6 +2129,38 @@ _public_ int sd_event_add_memory_pressure(
         return 0;
 }
 
+_public_ int sd_event_add_memory_pressure(
+                sd_event *e,
+                sd_event_source **ret,
+                sd_event_handler_t callback,
+                void *userdata) {
+
+        return event_add_pressure(
+                        e, ret, callback, userdata,
+                        SOURCE_MEMORY_PRESSURE,
+                        memory_pressure_callback,
+                        PRESSURE_MEMORY);
+}
+
+static int cpu_pressure_callback(sd_event_source *s, void *userdata) {
+        assert(s);
+
+        return 0;
+}
+
+_public_ int sd_event_add_cpu_pressure(
+                sd_event *e,
+                sd_event_source **ret,
+                sd_event_handler_t callback,
+                void *userdata) {
+
+        return event_add_pressure(
+                        e, ret, callback, userdata,
+                        SOURCE_CPU_PRESSURE,
+                        cpu_pressure_callback,
+                        PRESSURE_CPU);
+}
+
 static void event_free_inotify_data(sd_event *e, InotifyData *d) {
         assert(e);
 
@@ -2910,7 +2962,8 @@ static int event_source_offline(
                 break;
 
         case SOURCE_MEMORY_PRESSURE:
-                source_memory_pressure_unregister(s);
+        case SOURCE_CPU_PRESSURE:
+                source_pressure_unregister(s);
                 break;
 
         case SOURCE_TIME_REALTIME:
@@ -3001,10 +3054,11 @@ static int event_source_online(
                 break;
 
         case SOURCE_MEMORY_PRESSURE:
-                /* As documented in sd_event_add_memory_pressure(), we can only register the PSI fd with
-                 * epoll after writing the watch string. */
-                if (s->memory_pressure.write_buffer_size == 0) {
-                        r = source_memory_pressure_register(s, enabled);
+        case SOURCE_CPU_PRESSURE:
+                /* As documented in sd_event_add_{memory,cpu,io}_pressure(), we can only register the PSI fd
+                 * with epoll after writing the watch string. */
+                if (s->pressure.write_buffer_size == 0) {
+                        r = source_pressure_register(s, enabled);
                         if (r < 0)
                                 return r;
                 }
@@ -3986,30 +4040,30 @@ static int process_inotify(sd_event *e) {
         return done;
 }
 
-static int process_memory_pressure(sd_event_source *s, uint32_t revents) {
+static int process_pressure(sd_event_source *s, uint32_t revents) {
         assert(s);
-        assert(s->type == SOURCE_MEMORY_PRESSURE);
+        assert(EVENT_SOURCE_IS_PRESSURE(s));
 
         if (s->pending)
-                s->memory_pressure.revents |= revents;
+                s->pressure.revents |= revents;
         else
-                s->memory_pressure.revents = revents;
+                s->pressure.revents = revents;
 
         return source_set_pending(s, true);
 }
 
-static int source_memory_pressure_write(sd_event_source *s) {
+static int source_pressure_write(sd_event_source *s) {
         ssize_t n;
         int r;
 
         assert(s);
-        assert(s->type == SOURCE_MEMORY_PRESSURE);
+        assert(EVENT_SOURCE_IS_PRESSURE(s));
 
         /* once we start writing, the buffer is locked, we allow no further changes. */
-        s->memory_pressure.locked = true;
+        s->pressure.locked = true;
 
-        if (s->memory_pressure.write_buffer_size > 0) {
-                n = write(s->memory_pressure.fd, s->memory_pressure.write_buffer, s->memory_pressure.write_buffer_size);
+        if (s->pressure.write_buffer_size > 0) {
+                n = write(s->pressure.fd, s->pressure.write_buffer, s->pressure.write_buffer_size);
                 if (n < 0) {
                         if (!ERRNO_IS_TRANSIENT(errno)) {
                                 /* If kernel is built with CONFIG_PSI_DEFAULT_DISABLED it will expose PSI
@@ -4018,7 +4072,7 @@ static int source_memory_pressure_write(sd_event_source *s) {
                                  * so late. Let's make the best of it, and turn off the event source like we
                                  * do for failed event source handlers. */
 
-                                log_debug_errno(errno, "Writing memory pressure settings to kernel failed, disabling memory pressure event source: %m");
+                                log_debug_errno(errno, "Writing pressure settings to kernel failed, disabling pressure event source: %m");
                                 assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
                                 return 0;
                         }
@@ -4030,41 +4084,41 @@ static int source_memory_pressure_write(sd_event_source *s) {
 
         assert(n >= 0);
 
-        if ((size_t) n == s->memory_pressure.write_buffer_size) {
-                s->memory_pressure.write_buffer = mfree(s->memory_pressure.write_buffer);
+        if ((size_t) n == s->pressure.write_buffer_size) {
+                s->pressure.write_buffer = mfree(s->pressure.write_buffer);
 
                 if (n > 0) {
-                        s->memory_pressure.write_buffer_size = 0;
+                        s->pressure.write_buffer_size = 0;
 
                         /* Update epoll events mask, since we have now written everything and don't care for EPOLLOUT anymore */
-                        r = source_memory_pressure_register(s, s->enabled);
+                        r = source_pressure_register(s, s->enabled);
                         if (r < 0)
                                 return r;
                 }
         } else if (n > 0) {
                 _cleanup_free_ void *c = NULL;
 
-                assert((size_t) n < s->memory_pressure.write_buffer_size);
+                assert((size_t) n < s->pressure.write_buffer_size);
 
-                c = memdup((uint8_t*) s->memory_pressure.write_buffer + n, s->memory_pressure.write_buffer_size - n);
+                c = memdup((uint8_t*) s->pressure.write_buffer + n, s->pressure.write_buffer_size - n);
                 if (!c)
                         return -ENOMEM;
 
-                free_and_replace(s->memory_pressure.write_buffer, c);
-                s->memory_pressure.write_buffer_size -= n;
+                free_and_replace(s->pressure.write_buffer, c);
+                s->pressure.write_buffer_size -= n;
                 return 1;
         }
 
         return 0;
 }
 
-static int source_memory_pressure_initiate_dispatch(sd_event_source *s) {
+static int source_pressure_initiate_dispatch(sd_event_source *s) {
         int r;
 
         assert(s);
-        assert(s->type == SOURCE_MEMORY_PRESSURE);
+        assert(EVENT_SOURCE_IS_PRESSURE(s));
 
-        r = source_memory_pressure_write(s);
+        r = source_pressure_write(s);
         if (r < 0)
                 return r;
         if (r > 0)
@@ -4072,22 +4126,22 @@ static int source_memory_pressure_initiate_dispatch(sd_event_source *s) {
                            * function. Instead, shortcut it so that we wait for next EPOLLOUT immediately. */
 
         /* No pending incoming IO? Then let's not continue further */
-        if ((s->memory_pressure.revents & (EPOLLIN|EPOLLPRI)) == 0) {
+        if ((s->pressure.revents & (EPOLLIN|EPOLLPRI)) == 0) {
 
                 /* Treat IO errors on the notifier the same ways errors returned from a callback */
-                if ((s->memory_pressure.revents & (EPOLLHUP|EPOLLERR|EPOLLRDHUP)) != 0)
+                if ((s->pressure.revents & (EPOLLHUP|EPOLLERR|EPOLLRDHUP)) != 0)
                         return -EIO;
 
                 return 1; /* leave dispatch, we already processed everything */
         }
 
-        if (s->memory_pressure.revents & EPOLLIN) {
+        if (s->pressure.revents & EPOLLIN) {
                 uint8_t pipe_buf[PIPE_BUF];
                 ssize_t n;
 
                 /* If the fd is readable, then flush out anything that might be queued */
 
-                n = read(s->memory_pressure.fd, pipe_buf, sizeof(pipe_buf));
+                n = read(s->pressure.fd, pipe_buf, sizeof(pipe_buf));
                 if (n < 0 && !ERRNO_IS_TRANSIENT(errno))
                         return -errno;
         }
@@ -4158,8 +4212,8 @@ static int source_dispatch(sd_event_source *s) {
         if (r < 0)
                 return r;
 
-        if (s->type == SOURCE_MEMORY_PRESSURE) {
-                r = source_memory_pressure_initiate_dispatch(s);
+        if (EVENT_SOURCE_IS_PRESSURE(s)) {
+                r = source_pressure_initiate_dispatch(s);
                 if (r == -EIO) /* handle EIO errors similar to callback errors */
                         goto finish;
                 if (r < 0)
@@ -4254,7 +4308,8 @@ static int source_dispatch(sd_event_source *s) {
         }
 
         case SOURCE_MEMORY_PRESSURE:
-                r = s->memory_pressure.callback(s, s->userdata);
+        case SOURCE_CPU_PRESSURE:
+                r = s->pressure.callback(s, s->userdata);
                 break;
 
         case SOURCE_WATCHDOG:
@@ -4422,7 +4477,7 @@ static void event_close_inode_data_fds(sd_event *e) {
         }
 }
 
-static int event_memory_pressure_write_list(sd_event *e) {
+static int event_pressure_write_list(sd_event *e) {
         int r;
 
         assert(e);
@@ -4430,15 +4485,15 @@ static int event_memory_pressure_write_list(sd_event *e) {
         for (;;) {
                 sd_event_source *s;
 
-                s = LIST_POP(memory_pressure.write_list, e->memory_pressure_write_list);
+                s = LIST_POP(pressure.write_list, e->pressure_write_list);
                 if (!s)
                         break;
 
-                assert(s->type == SOURCE_MEMORY_PRESSURE);
-                assert(s->memory_pressure.write_buffer_size > 0);
-                s->memory_pressure.in_write_list = false;
+                assert(EVENT_SOURCE_IS_PRESSURE(s));
+                assert(s->pressure.write_buffer_size > 0);
+                s->pressure.in_write_list = false;
 
-                r = source_memory_pressure_write(s);
+                r = source_pressure_write(s);
                 if (r < 0)
                         return r;
         }
@@ -4499,7 +4554,7 @@ _public_ int sd_event_prepare(sd_event *e) {
         if (r < 0)
                 return r;
 
-        r = event_memory_pressure_write_list(e);
+        r = event_pressure_write_list(e);
         if (r < 0)
                 return r;
 
@@ -4668,7 +4723,8 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
                                         break;
 
                                 case SOURCE_MEMORY_PRESSURE:
-                                        r = process_memory_pressure(s, i->events);
+                                case SOURCE_CPU_PRESSURE:
+                                        r = process_pressure(s, i->events);
                                         break;
 
                                 default:
@@ -5306,27 +5362,27 @@ _public_ int sd_event_get_exit_on_idle(sd_event *e) {
         return e->exit_on_idle;
 }
 
-_public_ int sd_event_source_set_memory_pressure_type(sd_event_source *s, const char *ty) {
+static int event_source_set_pressure_type(sd_event_source *s, const char *ty) {
         _cleanup_free_ char *b = NULL;
         _cleanup_free_ void *w = NULL;
 
         assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_MEMORY_PRESSURE, -EDOM);
+        assert_return(EVENT_SOURCE_IS_PRESSURE(s), -EDOM);
         assert_return(ty, -EINVAL);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
         if (!STR_IN_SET(ty, "some", "full"))
                 return -EINVAL;
 
-        if (s->memory_pressure.locked) /* Refuse adjusting parameters, if caller told us how to watch for events */
+        if (s->pressure.locked) /* Refuse adjusting parameters, if caller told us how to watch for events */
                 return -EBUSY;
 
-        char* space = memchr(s->memory_pressure.write_buffer, ' ', s->memory_pressure.write_buffer_size);
+        char* space = memchr(s->pressure.write_buffer, ' ', s->pressure.write_buffer_size);
         if (!space)
                 return -EINVAL;
 
-        size_t l = space - (char*) s->memory_pressure.write_buffer;
-        b = memdup_suffix0(s->memory_pressure.write_buffer, l);
+        size_t l = space - (char*) s->pressure.write_buffer;
+        b = memdup_suffix0(s->pressure.write_buffer, l);
         if (!b)
                 return -ENOMEM;
         if (!STR_IN_SET(b, "some", "full"))
@@ -5335,26 +5391,40 @@ _public_ int sd_event_source_set_memory_pressure_type(sd_event_source *s, const
         if (streq(b, ty))
                 return 0;
 
-        size_t nl = strlen(ty) + (s->memory_pressure.write_buffer_size - l);
+        size_t nl = strlen(ty) + (s->pressure.write_buffer_size - l);
         w = new(char, nl);
         if (!w)
                 return -ENOMEM;
 
-        memcpy(stpcpy(w, ty), space, (s->memory_pressure.write_buffer_size - l));
+        memcpy(stpcpy(w, ty), space, (s->pressure.write_buffer_size - l));
 
-        free_and_replace(s->memory_pressure.write_buffer, w);
-        s->memory_pressure.write_buffer_size = nl;
-        s->memory_pressure.locked = false;
+        free_and_replace(s->pressure.write_buffer, w);
+        s->pressure.write_buffer_size = nl;
+        s->pressure.locked = false;
 
         return 1;
 }
 
-_public_ int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec) {
+_public_ int sd_event_source_set_memory_pressure_type(sd_event_source *s, const char *ty) {
+        assert_return(s, -EINVAL);
+        assert_return(s->type == SOURCE_MEMORY_PRESSURE, -EDOM);
+
+        return event_source_set_pressure_type(s, ty);
+}
+
+_public_ int sd_event_source_set_cpu_pressure_type(sd_event_source *s, const char *ty) {
+        assert_return(s, -EINVAL);
+        assert_return(s->type == SOURCE_CPU_PRESSURE, -EDOM);
+
+        return event_source_set_pressure_type(s, ty);
+}
+
+static int event_source_set_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec) {
         _cleanup_free_ char *b = NULL;
         _cleanup_free_ void *w = NULL;
 
         assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_MEMORY_PRESSURE, -EDOM);
+        assert_return(EVENT_SOURCE_IS_PRESSURE(s), -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
         if (threshold_usec <= 0 || threshold_usec >= UINT64_MAX)
@@ -5364,15 +5434,15 @@ _public_ int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint
         if (threshold_usec > window_usec)
                 return -EINVAL;
 
-        if (s->memory_pressure.locked) /* Refuse adjusting parameters, if caller told us how to watch for events */
+        if (s->pressure.locked) /* Refuse adjusting parameters, if caller told us how to watch for events */
                 return -EBUSY;
 
-        char* space = memchr(s->memory_pressure.write_buffer, ' ', s->memory_pressure.write_buffer_size);
+        char* space = memchr(s->pressure.write_buffer, ' ', s->pressure.write_buffer_size);
         if (!space)
                 return -EINVAL;
 
-        size_t l = space - (char*) s->memory_pressure.write_buffer;
-        b = memdup_suffix0(s->memory_pressure.write_buffer, l);
+        size_t l = space - (char*) s->pressure.write_buffer;
+        b = memdup_suffix0(s->pressure.write_buffer, l);
         if (!b)
                 return -ENOMEM;
         if (!STR_IN_SET(b, "some", "full"))
@@ -5386,12 +5456,26 @@ _public_ int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint
                 return -EINVAL;
 
         l = strlen(w) + 1;
-        if (memcmp_nn(s->memory_pressure.write_buffer, s->memory_pressure.write_buffer_size, w, l) == 0)
+        if (memcmp_nn(s->pressure.write_buffer, s->pressure.write_buffer_size, w, l) == 0)
                 return 0;
 
-        free_and_replace(s->memory_pressure.write_buffer, w);
-        s->memory_pressure.write_buffer_size = l;
-        s->memory_pressure.locked = false;
+        free_and_replace(s->pressure.write_buffer, w);
+        s->pressure.write_buffer_size = l;
+        s->pressure.locked = false;
 
         return 1;
 }
+
+_public_ int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec) {
+        assert_return(s, -EINVAL);
+        assert_return(s->type == SOURCE_MEMORY_PRESSURE, -EDOM);
+
+        return event_source_set_pressure_period(s, threshold_usec, window_usec);
+}
+
+_public_ int sd_event_source_set_cpu_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec) {
+        assert_return(s, -EINVAL);
+        assert_return(s->type == SOURCE_CPU_PRESSURE, -EDOM);
+
+        return event_source_set_pressure_period(s, threshold_usec, window_usec);
+}
index f5c79acdfdabcb52f93f9b6eacb5de3af9314e9e..71fc9504889e621da4f29f23a0a5cb17addf495d 100644 (file)
@@ -97,6 +97,7 @@ int sd_event_add_defer(sd_event *e, sd_event_source **ret, sd_event_handler_t ca
 int sd_event_add_post(sd_event *e, sd_event_source **ret, sd_event_handler_t callback, void *userdata);
 int sd_event_add_exit(sd_event *e, sd_event_source **ret, sd_event_handler_t callback, void *userdata);
 int sd_event_add_memory_pressure(sd_event *e, sd_event_source **ret, sd_event_handler_t callback, void *userdata);
+int sd_event_add_cpu_pressure(sd_event *e, sd_event_source **ret, sd_event_handler_t callback, void *userdata);
 
 int sd_event_prepare(sd_event *e);
 int sd_event_wait(sd_event *e, uint64_t timeout);
@@ -162,6 +163,8 @@ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
 int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret);
 int sd_event_source_set_memory_pressure_type(sd_event_source *s, const char *ty);
 int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec);
+int sd_event_source_set_cpu_pressure_type(sd_event_source *s, const char *ty);
+int sd_event_source_set_cpu_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec);
 int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);
 int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret);
 int sd_event_source_get_floating(sd_event_source *s);
index aa6e5b97f44e880dffeb4f3a10060834fe281107..fbb730ab5e14801c475ad8586dd89c8b523f35fd 100644 (file)
@@ -368,7 +368,7 @@ executables += [
                 'dependencies' : libm,
         },
         test_template + {
-                'sources' : files('test-mempress.c'),
+                'sources' : files('test-pressure.c'),
                 'dependencies' : threads,
         },
         test_template + {
similarity index 58%
rename from src/test/test-mempress.c
rename to src/test/test-pressure.c
index f8e58bf1e48e2887946cc94a0c919c5e8f387676..44ff810753e8aefd0ef98c3a7d60c33d6172e5ca 100644 (file)
@@ -28,6 +28,8 @@
 #include "tmpfile-util.h"
 #include "unit-def.h"
 
+/* Shared infrastructure for fake pressure tests */
+
 struct fake_pressure_context {
         int fifo_fd;
         int socket_fd;
@@ -62,7 +64,7 @@ static int fake_pressure_callback(sd_event_source *s, void *userdata) {
 
         *value *= d[0];
 
-        log_notice("memory pressure event: %s", d);
+        log_notice("pressure event: %s", d);
 
         if (*value == 7 * 'f' * 's')
                 ASSERT_OK(sd_event_exit(sd_event_source_get_event(s), 0));
@@ -70,7 +72,12 @@ static int fake_pressure_callback(sd_event_source *s, void *userdata) {
         return 0;
 }
 
-TEST(fake_pressure) {
+typedef int (*event_add_pressure_t)(sd_event *, sd_event_source **, sd_event_handler_t, void *);
+
+static void test_fake_pressure(
+                const char *resource,
+                event_add_pressure_t add_pressure) {
+
         _cleanup_(sd_event_source_unrefp) sd_event_source *es = NULL, *ef = NULL;
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL;
@@ -79,6 +86,12 @@ TEST(fake_pressure) {
         pthread_t th;
         int value = 7;
 
+        _cleanup_free_ char *resource_upper = ASSERT_NOT_NULL(strdup(resource));
+        ascii_strupper(resource_upper);
+
+        _cleanup_free_ char *env_watch = ASSERT_NOT_NULL(strjoin(resource_upper, "_PRESSURE_WATCH")),
+                            *env_write = ASSERT_NOT_NULL(strjoin(resource_upper, "_PRESSURE_WRITE"));
+
         ASSERT_OK(sd_event_default(&e));
 
         ASSERT_OK(mkdtemp_malloc(NULL, &tmp));
@@ -106,16 +119,16 @@ TEST(fake_pressure) {
 
         ASSERT_EQ(pthread_create(&th, NULL, fake_pressure_thread, TAKE_PTR(fp)), 0);
 
-        ASSERT_OK_ERRNO(setenv("MEMORY_PRESSURE_WATCH", j, /* override= */ true));
-        ASSERT_OK_ERRNO(unsetenv("MEMORY_PRESSURE_WRITE"));
+        ASSERT_OK_ERRNO(setenv(env_watch, j, /* override= */ true));
+        ASSERT_OK_ERRNO(unsetenv(env_write));
 
-        ASSERT_OK(sd_event_add_memory_pressure(e, &es, fake_pressure_callback, &value));
+        ASSERT_OK(add_pressure(e, &es, fake_pressure_callback, &value));
         ASSERT_OK(sd_event_source_set_description(es, "fifo event source"));
 
-        ASSERT_OK_ERRNO(setenv("MEMORY_PRESSURE_WATCH", k, /* override= */ true));
-        ASSERT_OK_ERRNO(setenv("MEMORY_PRESSURE_WRITE", "aGVsbG8K", /* override= */ true));
+        ASSERT_OK_ERRNO(setenv(env_watch, k, /* override= */ true));
+        ASSERT_OK_ERRNO(setenv(env_write, "aGVsbG8K", /* override= */ true));
 
-        ASSERT_OK(sd_event_add_memory_pressure(e, &ef, fake_pressure_callback, &value));
+        ASSERT_OK(add_pressure(e, &ef, fake_pressure_callback, &value));
         ASSERT_OK(sd_event_source_set_description(ef, "socket event source"));
 
         ASSERT_OK(sd_event_loop(e));
@@ -125,18 +138,52 @@ TEST(fake_pressure) {
         ASSERT_EQ(pthread_join(th, NULL), 0);
 }
 
+static int fake_pressure_wrapper(sd_event *e, sd_event_source **ret, sd_event_handler_t callback, void *userdata) {
+        return sd_event_add_memory_pressure(e, ret, callback, userdata);
+}
+
+TEST(fake_memory_pressure) {
+        test_fake_pressure("memory", fake_pressure_wrapper);
+}
+
+static int fake_cpu_pressure_wrapper(sd_event *e, sd_event_source **ret, sd_event_handler_t callback, void *userdata) {
+        return sd_event_add_cpu_pressure(e, ret, callback, userdata);
+}
+
+TEST(fake_cpu_pressure) {
+        test_fake_pressure("cpu", fake_cpu_pressure_wrapper);
+}
+
+/* Shared infrastructure for real pressure tests */
+
 struct real_pressure_context {
         sd_event_source *pid;
 };
 
-static int real_pressure_callback(sd_event_source *s, void *userdata) {
+static int real_pressure_child_callback(sd_event_source *s, const siginfo_t *si, void *userdata) {
+        ASSERT_NOT_NULL(s);
+        ASSERT_NOT_NULL(si);
+
+        log_notice("child dead");
+
+        ASSERT_EQ(si->si_signo, SIGCHLD);
+        ASSERT_EQ(si->si_status, SIGKILL);
+        ASSERT_EQ(si->si_code, CLD_KILLED);
+
+        ASSERT_OK(sd_event_exit(sd_event_source_get_event(s), 31));
+        return 0;
+}
+
+/* Memory pressure real test */
+
+static int real_memory_pressure_callback(sd_event_source *s, void *userdata) {
         struct real_pressure_context *c = ASSERT_PTR(userdata);
         const char *d;
 
         ASSERT_NOT_NULL(s);
         ASSERT_OK(sd_event_source_get_description(s, &d));
 
-        log_notice("real_memory pressure event: %s", d);
+        log_notice("real memory pressure event: %s", d);
 
         sd_event_trim_memory();
 
@@ -174,21 +221,7 @@ _noreturn_ static void real_pressure_eat_memory(int pipe_fd) {
         }
 }
 
-static int real_pressure_child_callback(sd_event_source *s, const siginfo_t *si, void *userdata) {
-        ASSERT_NOT_NULL(s);
-        ASSERT_NOT_NULL(si);
-
-        log_notice("child dead");
-
-        ASSERT_EQ(si->si_signo, SIGCHLD);
-        ASSERT_EQ(si->si_status, SIGKILL);
-        ASSERT_EQ(si->si_code, CLD_KILLED);
-
-        ASSERT_OK(sd_event_exit(sd_event_source_get_event(s), 31));
-        return 0;
-}
-
-TEST(real_pressure) {
+TEST(real_memory_pressure) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_event_source_unrefp) sd_event_source *es = NULL, *cs = NULL;
@@ -200,9 +233,12 @@ TEST(real_pressure) {
         const char *object;
         int r;
 
-        r = sd_bus_open_system(&bus);
+        if (getuid() == 0)
+                r = sd_bus_open_system(&bus);
+        else
+                r = sd_bus_open_user(&bus);
         if (r < 0)
-                return (void) log_tests_skipped_errno(r, "can't connect to system bus");
+                return (void) log_tests_skipped_errno(r, "can't connect to bus");
 
         ASSERT_OK(bus_wait_for_jobs_new(bus, &w));
 
@@ -245,7 +281,7 @@ TEST(real_pressure) {
                 .pid = cs,
         };
 
-        r = sd_event_add_memory_pressure(e, &es, real_pressure_callback, &context);
+        r = sd_event_add_memory_pressure(e, &es, real_memory_pressure_callback, &context);
         if (r < 0)
                 return (void) log_tests_skipped_errno(r, "can't allocate memory pressure fd");
 
@@ -255,8 +291,9 @@ TEST(real_pressure) {
         ASSERT_OK_ZERO(sd_event_source_set_memory_pressure_type(es, "full"));
         ASSERT_OK_POSITIVE(sd_event_source_set_memory_pressure_type(es, "some"));
         ASSERT_OK_ZERO(sd_event_source_set_memory_pressure_type(es, "some"));
-        ASSERT_OK_POSITIVE(sd_event_source_set_memory_pressure_period(es, 70 * USEC_PER_MSEC, USEC_PER_SEC));
-        ASSERT_OK_ZERO(sd_event_source_set_memory_pressure_period(es, 70 * USEC_PER_MSEC, USEC_PER_SEC));
+        /* Unprivileged writes require a minimum of 2s otherwise the kernel will refuse the write.  */
+        ASSERT_OK_POSITIVE(sd_event_source_set_memory_pressure_period(es, 70 * USEC_PER_MSEC, 2 * USEC_PER_SEC));
+        ASSERT_OK_ZERO(sd_event_source_set_memory_pressure_period(es, 70 * USEC_PER_MSEC, 2 * USEC_PER_SEC));
         ASSERT_OK(sd_event_source_set_enabled(es, SD_EVENT_ONESHOT));
 
         _cleanup_free_ char *uo = NULL;
@@ -298,6 +335,123 @@ TEST(real_pressure) {
         ASSERT_EQ(ex, 31);
 }
 
+/* CPU pressure real test */
+
+static int real_cpu_pressure_callback(sd_event_source *s, void *userdata) {
+        struct real_pressure_context *c = ASSERT_PTR(userdata);
+        const char *d;
+
+        ASSERT_NOT_NULL(s);
+        ASSERT_OK(sd_event_source_get_description(s, &d));
+
+        log_notice("real cpu pressure event: %s", d);
+
+        ASSERT_NOT_NULL(c->pid);
+        ASSERT_OK(sd_event_source_send_child_signal(c->pid, SIGKILL, NULL, 0));
+        c->pid = NULL;
+
+        return 0;
+}
+
+_noreturn_ static void real_pressure_eat_cpu(int pipe_fd) {
+        char x;
+        ASSERT_EQ(read(pipe_fd, &x, 1), 1); /* Wait for the GO! */
+
+        /* Busy-loop to generate CPU pressure */
+        for (;;)
+                __asm__ volatile("" ::: "memory"); /* Prevent optimization */
+}
+
+TEST(real_cpu_pressure) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *es = NULL, *cs = NULL;
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_close_pair_ int pipe_fd[2] = EBADF_PAIR;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_free_ char *scope = NULL;
+        const char *object;
+        int r;
+
+        if (getuid() == 0)
+                r = sd_bus_open_system(&bus);
+        else
+                r = sd_bus_open_user(&bus);
+        if (r < 0)
+                return (void) log_tests_skipped_errno(r, "can't connect to bus");
+
+        ASSERT_OK(bus_wait_for_jobs_new(bus, &w));
+
+        ASSERT_OK(bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit"));
+        ASSERT_OK(asprintf(&scope, "test-%" PRIu64 ".scope", random_u64()));
+        ASSERT_OK(sd_bus_message_append(m, "ss", scope, "fail"));
+        ASSERT_OK(sd_bus_message_open_container(m, 'a', "(sv)"));
+        ASSERT_OK(sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, 0));
+        ASSERT_OK(sd_bus_message_append(m, "(sv)", "CPUAccounting", "b", true));
+        ASSERT_OK(sd_bus_message_close_container(m));
+        ASSERT_OK(sd_bus_message_append(m, "a(sa(sv))", 0));
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0)
+                return (void) log_tests_skipped_errno(r, "can't issue transient unit call");
+
+        ASSERT_OK(sd_bus_message_read(reply, "o", &object));
+
+        ASSERT_OK(bus_wait_for_jobs_one(w, object, /* flags= */ BUS_WAIT_JOBS_LOG_ERROR, /* extra_args= */ NULL));
+
+        ASSERT_OK(sd_event_default(&e));
+
+        ASSERT_OK_ERRNO(pipe2(pipe_fd, O_CLOEXEC));
+
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        r = pidref_safe_fork("(eat-cpu)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, &pidref);
+        ASSERT_OK(r);
+        if (r == 0) {
+                real_pressure_eat_cpu(pipe_fd[0]);
+                _exit(EXIT_SUCCESS);
+        }
+
+        ASSERT_OK(event_add_child_pidref(e, &cs, &pidref, WEXITED, real_pressure_child_callback, NULL));
+        ASSERT_OK(sd_event_source_set_child_process_own(cs, true));
+
+        ASSERT_OK_ERRNO(unsetenv("CPU_PRESSURE_WATCH"));
+        ASSERT_OK_ERRNO(unsetenv("CPU_PRESSURE_WRITE"));
+
+        struct real_pressure_context context = {
+                .pid = cs,
+        };
+
+        r = sd_event_add_cpu_pressure(e, &es, real_cpu_pressure_callback, &context);
+        if (r < 0)
+                return (void) log_tests_skipped_errno(r, "can't allocate cpu pressure fd");
+
+        ASSERT_OK(sd_event_source_set_description(es, "real pressure event source"));
+        ASSERT_OK_ZERO(sd_event_source_set_cpu_pressure_type(es, "some"));
+        /* Unprivileged writes require a minimum of 2s otherwise the kernel will refuse the write. */
+        ASSERT_OK_POSITIVE(sd_event_source_set_cpu_pressure_period(es, 70 * USEC_PER_MSEC, 2 * USEC_PER_SEC));
+        ASSERT_OK_ZERO(sd_event_source_set_cpu_pressure_period(es, 70 * USEC_PER_MSEC, 2 * USEC_PER_SEC));
+        ASSERT_OK(sd_event_source_set_enabled(es, SD_EVENT_ONESHOT));
+
+        m = sd_bus_message_unref(m);
+
+        ASSERT_OK(bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetUnitProperties"));
+        ASSERT_OK(sd_bus_message_append(m, "sb", scope, true));
+        ASSERT_OK(sd_bus_message_open_container(m, 'a', "(sv)"));
+        ASSERT_OK(sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (uint64_t) 1000)); /* 0.1% CPU */
+        ASSERT_OK(sd_bus_message_close_container(m));
+
+        ASSERT_OK(sd_bus_call(bus, m, 0, NULL, NULL));
+
+        /* Now start eating CPU */
+        ASSERT_EQ(write(pipe_fd[1], &(const char) { 'x' }, 1), 1);
+
+        ASSERT_OK(sd_event_loop(e));
+        int ex = 0;
+        ASSERT_OK(sd_event_get_exit_code(e, &ex));
+        ASSERT_EQ(ex, 31);
+}
+
 static int outro(void) {
         hashmap_trim_pools();
         return 0;