test-mempress: Support unprivileged operation
This commit is contained in:
committed by
Daan De Meyer
parent
1121c77526
commit
b3bb4fefde
@@ -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'],
|
||||
''],
|
||||
|
||||
@@ -21,7 +21,11 @@
|
||||
<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>
|
||||
@@ -51,6 +55,27 @@
|
||||
<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>
|
||||
@@ -62,12 +87,14 @@
|
||||
<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
|
||||
@@ -83,12 +110,13 @@
|
||||
<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
|
||||
@@ -111,6 +139,13 @@
|
||||
<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>
|
||||
@@ -143,7 +178,7 @@
|
||||
|
||||
<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
|
||||
@@ -152,6 +187,16 @@
|
||||
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
|
||||
@@ -197,8 +242,9 @@
|
||||
<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>
|
||||
@@ -207,7 +253,8 @@
|
||||
<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>
|
||||
@@ -216,7 +263,8 @@
|
||||
<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>
|
||||
@@ -225,8 +273,9 @@
|
||||
<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>
|
||||
@@ -234,8 +283,10 @@
|
||||
<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>
|
||||
@@ -277,6 +328,9 @@
|
||||
<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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
+22
-4
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+209
-125
@@ -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);
|
||||
|
||||
if (!callback)
|
||||
callback = memory_pressure_callback;
|
||||
assert(resource >= 0 && resource < _PRESSURE_RESOURCE_MAX);
|
||||
const PressureResourceInfo *info = &pressure_resource_info[resource];
|
||||
|
||||
s = source_new(e, !ret, SOURCE_MEMORY_PRESSURE);
|
||||
if (!callback)
|
||||
callback = default_callback;
|
||||
|
||||
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. */
|
||||
* 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->memory_pressure.write_buffer_size > 0)
|
||||
source_memory_pressure_add_to_write_list(s);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -368,7 +368,7 @@ executables += [
|
||||
'dependencies' : libm,
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-mempress.c'),
|
||||
'sources' : files('test-pressure.c'),
|
||||
'dependencies' : threads,
|
||||
},
|
||||
test_template + {
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user