1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
11 #include "bus-locator.h"
12 #include "bus-wait-for-jobs.h"
14 #include "path-util.h"
15 #include "process-util.h"
16 #include "random-util.h"
18 #include "signal-util.h"
19 #include "socket-util.h"
21 #include "tmpfile-util.h"
24 struct fake_pressure_context
{
29 static void *fake_pressure_thread(void *p
) {
30 _cleanup_free_
struct fake_pressure_context
*c
= ASSERT_PTR(p
);
31 _cleanup_close_
int cfd
= -EBADF
;
35 assert_se(write(c
->fifo_fd
, &(const char) { 'x' }, 1) == 1);
39 cfd
= accept4(c
->socket_fd
, NULL
, NULL
, SOCK_CLOEXEC
);
41 char buf
[STRLEN("hello")+1] = {};
42 assert_se(read(cfd
, buf
, sizeof(buf
)-1) == sizeof(buf
)-1);
43 assert_se(streq(buf
, "hello"));
44 assert_se(write(cfd
, &(const char) { 'z' }, 1) == 1);
49 static int fake_pressure_callback(sd_event_source
*s
, void *userdata
) {
50 int *value
= userdata
;
54 assert_se(sd_event_source_get_description(s
, &d
) >= 0);
58 log_notice("memory pressure event: %s", d
);
60 if (*value
== 7 * 'f' * 's')
61 assert_se(sd_event_exit(sd_event_source_get_event(s
), 0) >= 0);
67 _cleanup_(sd_event_source_unrefp
) sd_event_source
*es
= NULL
, *ef
= NULL
;
68 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
69 _cleanup_free_
char *j
= NULL
, *k
= NULL
;
70 _cleanup_(rm_rf_physical_and_freep
) char *tmp
= NULL
;
71 _cleanup_close_
int fifo_fd
= -EBADF
, socket_fd
= -EBADF
;
72 union sockaddr_union sa
;
76 assert_se(sd_event_default(&e
) >= 0);
78 assert_se(mkdtemp_malloc(NULL
, &tmp
) >= 0);
80 assert_se(j
= path_join(tmp
, "fifo"));
81 assert_se(mkfifo(j
, 0600) >= 0);
82 fifo_fd
= open(j
, O_CLOEXEC
|O_RDWR
|O_NONBLOCK
);
83 assert_se(fifo_fd
>= 0);
85 assert_se(k
= path_join(tmp
, "sock"));
86 socket_fd
= socket(AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0);
87 assert_se(socket_fd
>= 0);
88 assert_se(sockaddr_un_set_path(&sa
.un
, k
) >= 0);
89 assert_se(bind(socket_fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
)) >= 0);
90 assert_se(listen(socket_fd
, 1) >= 0);
92 /* Ideally we'd just allocate this on the stack, but AddressSanitizer doesn't like it if threads
93 * access each other's stack */
94 struct fake_pressure_context
*fp
= new(struct fake_pressure_context
, 1);
96 *fp
= (struct fake_pressure_context
) {
98 .socket_fd
= socket_fd
,
101 assert_se(pthread_create(&th
, NULL
, fake_pressure_thread
, TAKE_PTR(fp
)) == 0);
103 assert_se(setenv("MEMORY_PRESSURE_WATCH", j
, /* override= */ true) >= 0);
104 assert_se(unsetenv("MEMORY_PRESSURE_WRITE") >= 0);
106 assert_se(sd_event_add_memory_pressure(e
, &es
, fake_pressure_callback
, &value
) >= 0);
107 assert_se(sd_event_source_set_description(es
, "fifo event source") >= 0);
109 assert_se(setenv("MEMORY_PRESSURE_WATCH", k
, /* override= */ true) >= 0);
110 assert_se(setenv("MEMORY_PRESSURE_WRITE", "aGVsbG8K", /* override= */ true) >= 0);
112 assert_se(sd_event_add_memory_pressure(e
, &ef
, fake_pressure_callback
, &value
) >= 0);
113 assert_se(sd_event_source_set_description(ef
, "socket event source") >= 0);
115 assert_se(sd_event_loop(e
) >= 0);
117 assert_se(value
== 7 * 'f' * 's');
119 assert_se(pthread_join(th
, NULL
) == 0);
122 struct real_pressure_context
{
123 sd_event_source
*pid
;
126 static int real_pressure_callback(sd_event_source
*s
, void *userdata
) {
127 struct real_pressure_context
*c
= ASSERT_PTR(userdata
);
131 assert_se(sd_event_source_get_description(s
, &d
) >= 0);
133 log_notice("real_memory pressure event: %s", d
);
135 sd_event_trim_memory();
138 assert_se(sd_event_source_send_child_signal(c
->pid
, SIGKILL
, NULL
, 0) >= 0);
144 #define MMAP_SIZE (10 * 1024 * 1024)
146 _noreturn_
static void real_pressure_eat_memory(int pipe_fd
) {
149 /* Allocates and touches 10M at a time, until runs out of memory */
152 assert_se(read(pipe_fd
, &x
, 1) == 1); /* Wait for the GO! */
157 p
= mmap(NULL
, MMAP_SIZE
, PROT_READ
|PROT_WRITE
, MAP_PRIVATE
|MAP_ANONYMOUS
, -1, 0);
158 assert_se(p
!= MAP_FAILED
);
160 log_info("Eating another %s.", FORMAT_BYTES(MMAP_SIZE
));
162 memset(p
, random_u32() & 0xFF, MMAP_SIZE
);
165 log_info("Ate %s in total.", FORMAT_BYTES(ate
));
167 usleep_safe(50 * USEC_PER_MSEC
);
171 static int real_pressure_child_callback(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
175 log_notice("child dead");
177 assert_se(si
->si_signo
== SIGCHLD
);
178 assert_se(si
->si_status
== SIGKILL
);
179 assert_se(si
->si_code
== CLD_KILLED
);
181 assert_se(sd_event_exit(sd_event_source_get_event(s
), 31) >= 0);
185 TEST(real_pressure
) {
186 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
187 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
188 _cleanup_(sd_event_source_unrefp
) sd_event_source
*es
= NULL
, *cs
= NULL
;
189 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
190 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
191 _cleanup_close_pair_
int pipe_fd
[2] = EBADF_PAIR
;
192 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
193 _cleanup_free_
char *scope
= NULL
;
198 r
= sd_bus_open_system(&bus
);
200 log_notice_errno(r
, "Can't connect to system bus, skipping test: %m");
204 assert_se(bus_wait_for_jobs_new(bus
, &w
) >= 0);
206 assert_se(bus_message_new_method_call(bus
, &m
, bus_systemd_mgr
, "StartTransientUnit") >= 0);
207 assert_se(asprintf(&scope
, "test-%" PRIu64
".scope", random_u64()) >= 0);
208 assert_se(sd_bus_message_append(m
, "ss", scope
, "fail") >= 0);
209 assert_se(sd_bus_message_open_container(m
, 'a', "(sv)") >= 0);
210 assert_se(sd_bus_message_append(m
, "(sv)", "PIDs", "au", 1, 0) >= 0);
211 assert_se(sd_bus_message_append(m
, "(sv)", "MemoryAccounting", "b", true) >= 0);
212 assert_se(sd_bus_message_close_container(m
) >= 0);
213 assert_se(sd_bus_message_append(m
, "a(sa(sv))", 0) >= 0);
215 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
217 log_notice_errno(r
, "Can't issue transient unit call, skipping test: %m");
221 assert_se(sd_bus_message_read(reply
, "o", &object
) >= 0);
223 assert_se(bus_wait_for_jobs_one(w
, object
, /* quiet= */ false, /* extra_args= */ NULL
) >= 0);
225 assert_se(sd_event_default(&e
) >= 0);
227 assert_se(pipe2(pipe_fd
, O_CLOEXEC
) >= 0);
229 r
= safe_fork("(eat-memory)", FORK_RESET_SIGNALS
|FORK_DEATHSIG_SIGTERM
, &pid
);
232 real_pressure_eat_memory(pipe_fd
[0]);
236 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
237 assert_se(sd_event_add_child(e
, &cs
, pid
, WEXITED
, real_pressure_child_callback
, NULL
) >= 0);
238 assert_se(sd_event_source_set_child_process_own(cs
, true) >= 0);
240 assert_se(unsetenv("MEMORY_PRESSURE_WATCH") >= 0);
241 assert_se(unsetenv("MEMORY_PRESSURE_WRITE") >= 0);
243 struct real_pressure_context context
= {
247 r
= sd_event_add_memory_pressure(e
, &es
, real_pressure_callback
, &context
);
249 log_notice_errno(r
, "Can't allocate memory pressure fd, skipping test: %m");
253 assert_se(sd_event_source_set_description(es
, "real pressure event source") >= 0);
254 assert_se(sd_event_source_set_memory_pressure_type(es
, "some") == 0);
255 assert_se(sd_event_source_set_memory_pressure_type(es
, "full") > 0);
256 assert_se(sd_event_source_set_memory_pressure_type(es
, "full") == 0);
257 assert_se(sd_event_source_set_memory_pressure_type(es
, "some") > 0);
258 assert_se(sd_event_source_set_memory_pressure_type(es
, "some") == 0);
259 assert_se(sd_event_source_set_memory_pressure_period(es
, 70 * USEC_PER_MSEC
, USEC_PER_SEC
) > 0);
260 assert_se(sd_event_source_set_memory_pressure_period(es
, 70 * USEC_PER_MSEC
, USEC_PER_SEC
) == 0);
261 assert_se(sd_event_source_set_enabled(es
, SD_EVENT_ONESHOT
) >= 0);
263 _cleanup_free_
char *uo
= NULL
;
264 assert_se(uo
= unit_dbus_path_from_name(scope
));
266 uint64_t mcurrent
= UINT64_MAX
;
267 assert_se(sd_bus_get_property_trivial(bus
, "org.freedesktop.systemd1", uo
, "org.freedesktop.systemd1.Scope", "MemoryCurrent", &error
, 't', &mcurrent
) >= 0);
269 printf("current: %" PRIu64
"\n", mcurrent
);
270 if (mcurrent
== UINT64_MAX
) {
271 log_notice_errno(r
, "Memory accounting not available, skipping test: %m");
275 m
= sd_bus_message_unref(m
);
277 assert_se(bus_message_new_method_call(bus
, &m
, bus_systemd_mgr
, "SetUnitProperties") >= 0);
278 assert_se(sd_bus_message_append(m
, "sb", scope
, true) >= 0);
279 assert_se(sd_bus_message_open_container(m
, 'a', "(sv)") >= 0);
280 assert_se(sd_bus_message_append(m
, "(sv)", "MemoryHigh", "t", mcurrent
+ (15 * 1024 * 1024)) >= 0);
281 assert_se(sd_bus_message_append(m
, "(sv)", "MemoryMax", "t", mcurrent
+ (50 * 1024 * 1024)) >= 0);
282 assert_se(sd_bus_message_close_container(m
) >= 0);
284 assert_se(sd_bus_call(bus
, m
, 0, NULL
, NULL
) >= 0);
286 /* Generate some memory allocations via mempool */
288 Hashmap
**h
= new(Hashmap
*, NN
);
289 for (int i
= 0; i
< NN
; i
++)
290 h
[i
] = hashmap_new(NULL
);
291 for (int i
= 0; i
< NN
; i
++)
295 /* Now start eating memory */
296 assert_se(write(pipe_fd
[1], &(const char) { 'x' }, 1) == 1);
298 assert_se(sd_event_loop(e
) >= 0);
300 assert_se(sd_event_get_exit_code(e
, &ex
) >= 0);
304 static int outro(void) {
305 hashmap_trim_pools();
309 DEFINE_TEST_MAIN_FULL(LOG_DEBUG
, NULL
, outro
);