]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-barrier.c
test-barrier: skip the test in virtualized/containerized environments
[thirdparty/systemd.git] / src / test / test-barrier.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 /*
4 * IPC barrier tests
5 * These tests verify the correct behavior of the IPC Barrier implementation.
6 * Note that the tests use alarm-timers to verify dead-locks and timeouts. These
7 * might not work on slow machines where 20ms are too short to perform specific
8 * operations (though, very unlikely). In case that turns out true, we have to
9 * increase it at the slightly cost of lengthen test-duration on other machines.
10 */
11
12 #include <stdio.h>
13 #include <sys/time.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16
17 #include "barrier.h"
18 #include "util.h"
19 #include "tests.h"
20 #include "virt.h"
21
22 /* 20ms to test deadlocks; All timings use multiples of this constant as
23 * alarm/sleep timers. If this timeout is too small for slow machines to perform
24 * the requested operations, we have to increase it. On an i7 this works fine
25 * with 1ms base-time, so 20ms should be just fine for everyone. */
26 #define BASE_TIME (20 * USEC_PER_MSEC)
27
28 static void set_alarm(usec_t usecs) {
29 struct itimerval v = { };
30
31 timeval_store(&v.it_value, usecs);
32 assert_se(setitimer(ITIMER_REAL, &v, NULL) >= 0);
33 }
34
35 static void sleep_for(usec_t usecs) {
36 /* stupid usleep() might fail if >1000000 */
37 assert_se(usecs < USEC_PER_SEC);
38 usleep(usecs);
39 }
40
41 #define TEST_BARRIER(_FUNCTION, _CHILD_CODE, _WAIT_CHILD, _PARENT_CODE, _WAIT_PARENT) \
42 static void _FUNCTION(void) { \
43 Barrier b = BARRIER_NULL; \
44 pid_t pid1, pid2; \
45 \
46 assert_se(barrier_create(&b) >= 0); \
47 assert_se(b.me > 0); \
48 assert_se(b.them > 0); \
49 assert_se(b.pipe[0] > 0); \
50 assert_se(b.pipe[1] > 0); \
51 \
52 pid1 = fork(); \
53 assert_se(pid1 >= 0); \
54 if (pid1 == 0) { \
55 barrier_set_role(&b, BARRIER_CHILD); \
56 { _CHILD_CODE; } \
57 exit(42); \
58 } \
59 \
60 pid2 = fork(); \
61 assert_se(pid2 >= 0); \
62 if (pid2 == 0) { \
63 barrier_set_role(&b, BARRIER_PARENT); \
64 { _PARENT_CODE; } \
65 exit(42); \
66 } \
67 \
68 barrier_destroy(&b); \
69 set_alarm(999999); \
70 { _WAIT_CHILD; } \
71 { _WAIT_PARENT; } \
72 set_alarm(0); \
73 }
74
75 #define TEST_BARRIER_WAIT_SUCCESS(_pid) \
76 ({ \
77 int pidr, status; \
78 pidr = waitpid(_pid, &status, 0); \
79 assert_se(pidr == _pid); \
80 assert_se(WIFEXITED(status)); \
81 assert_se(WEXITSTATUS(status) == 42); \
82 })
83
84 #define TEST_BARRIER_WAIT_ALARM(_pid) \
85 ({ \
86 int pidr, status; \
87 pidr = waitpid(_pid, &status, 0); \
88 assert_se(pidr == _pid); \
89 assert_se(WIFSIGNALED(status)); \
90 assert_se(WTERMSIG(status) == SIGALRM); \
91 })
92
93 /*
94 * Test basic sync points
95 * This places a barrier in both processes and waits synchronously for them.
96 * The timeout makes sure the sync works as expected. The sleep_for() on one side
97 * makes sure the exit of the parent does not overwrite previous barriers. Due
98 * to the sleep_for(), we know that the parent already exited, thus there's a
99 * pending HUP on the pipe. However, the barrier_sync() prefers reads on the
100 * eventfd, thus we can safely wait on the barrier.
101 */
102 TEST_BARRIER(test_barrier_sync,
103 ({
104 set_alarm(BASE_TIME * 10);
105 assert_se(barrier_place(&b));
106 sleep_for(BASE_TIME * 2);
107 assert_se(barrier_sync(&b));
108 }),
109 TEST_BARRIER_WAIT_SUCCESS(pid1),
110 ({
111 set_alarm(BASE_TIME * 10);
112 assert_se(barrier_place(&b));
113 assert_se(barrier_sync(&b));
114 }),
115 TEST_BARRIER_WAIT_SUCCESS(pid2));
116
117 /*
118 * Test wait_next()
119 * This places a barrier in the parent and syncs on it. The child sleeps while
120 * the parent places the barrier and then waits for a barrier. The wait will
121 * succeed as the child hasn't read the parent's barrier, yet. The following
122 * barrier and sync synchronize the exit.
123 */
124 TEST_BARRIER(test_barrier_wait_next,
125 ({
126 sleep_for(BASE_TIME);
127 set_alarm(BASE_TIME * 10);
128 assert_se(barrier_wait_next(&b));
129 assert_se(barrier_place(&b));
130 assert_se(barrier_sync(&b));
131 }),
132 TEST_BARRIER_WAIT_SUCCESS(pid1),
133 ({
134 set_alarm(BASE_TIME * 4);
135 assert_se(barrier_place(&b));
136 assert_se(barrier_sync(&b));
137 }),
138 TEST_BARRIER_WAIT_SUCCESS(pid2));
139
140 /*
141 * Test wait_next() multiple times
142 * This places two barriers in the parent and waits for the child to exit. The
143 * child sleeps 20ms so both barriers _should_ be in place. It then waits for
144 * the parent to place the next barrier twice. The first call will fetch both
145 * barriers and return. However, the second call will stall as the parent does
146 * not place a 3rd barrier (the sleep caught two barriers). wait_next() is does
147 * not look at barrier-links so this stall is expected. Thus this test times
148 * out.
149 */
150 TEST_BARRIER(test_barrier_wait_next_twice,
151 ({
152 sleep_for(BASE_TIME);
153 set_alarm(BASE_TIME);
154 assert_se(barrier_wait_next(&b));
155 assert_se(barrier_wait_next(&b));
156 assert_se(0);
157 }),
158 TEST_BARRIER_WAIT_ALARM(pid1),
159 ({
160 set_alarm(BASE_TIME * 10);
161 assert_se(barrier_place(&b));
162 assert_se(barrier_place(&b));
163 sleep_for(BASE_TIME * 4);
164 }),
165 TEST_BARRIER_WAIT_SUCCESS(pid2));
166
167 /*
168 * Test wait_next() with local barriers
169 * This is the same as test_barrier_wait_next_twice, but places local barriers
170 * between both waits. This does not have any effect on the wait so it times out
171 * like the other test.
172 */
173 TEST_BARRIER(test_barrier_wait_next_twice_local,
174 ({
175 sleep_for(BASE_TIME);
176 set_alarm(BASE_TIME);
177 assert_se(barrier_wait_next(&b));
178 assert_se(barrier_place(&b));
179 assert_se(barrier_place(&b));
180 assert_se(barrier_wait_next(&b));
181 assert_se(0);
182 }),
183 TEST_BARRIER_WAIT_ALARM(pid1),
184 ({
185 set_alarm(BASE_TIME * 10);
186 assert_se(barrier_place(&b));
187 assert_se(barrier_place(&b));
188 sleep_for(BASE_TIME * 4);
189 }),
190 TEST_BARRIER_WAIT_SUCCESS(pid2));
191
192 /*
193 * Test wait_next() with sync_next()
194 * This is again the same as test_barrier_wait_next_twice but uses a
195 * synced wait as the second wait. This works just fine because the local state
196 * has no barriers placed, therefore, the remote is always in sync.
197 */
198 TEST_BARRIER(test_barrier_wait_next_twice_sync,
199 ({
200 sleep_for(BASE_TIME);
201 set_alarm(BASE_TIME);
202 assert_se(barrier_wait_next(&b));
203 assert_se(barrier_sync_next(&b));
204 }),
205 TEST_BARRIER_WAIT_SUCCESS(pid1),
206 ({
207 set_alarm(BASE_TIME * 10);
208 assert_se(barrier_place(&b));
209 assert_se(barrier_place(&b));
210 }),
211 TEST_BARRIER_WAIT_SUCCESS(pid2));
212
213 /*
214 * Test wait_next() with sync_next() and local barriers
215 * This is again the same as test_barrier_wait_next_twice_local but uses a
216 * synced wait as the second wait. This works just fine because the local state
217 * is in sync with the remote.
218 */
219 TEST_BARRIER(test_barrier_wait_next_twice_local_sync,
220 ({
221 sleep_for(BASE_TIME);
222 set_alarm(BASE_TIME);
223 assert_se(barrier_wait_next(&b));
224 assert_se(barrier_place(&b));
225 assert_se(barrier_place(&b));
226 assert_se(barrier_sync_next(&b));
227 }),
228 TEST_BARRIER_WAIT_SUCCESS(pid1),
229 ({
230 set_alarm(BASE_TIME * 10);
231 assert_se(barrier_place(&b));
232 assert_se(barrier_place(&b));
233 }),
234 TEST_BARRIER_WAIT_SUCCESS(pid2));
235
236 /*
237 * Test sync_next() and sync()
238 * This tests sync_*() synchronizations and makes sure they work fine if the
239 * local state is behind the remote state.
240 */
241 TEST_BARRIER(test_barrier_sync_next,
242 ({
243 set_alarm(BASE_TIME * 10);
244 assert_se(barrier_sync_next(&b));
245 assert_se(barrier_sync(&b));
246 assert_se(barrier_place(&b));
247 assert_se(barrier_place(&b));
248 assert_se(barrier_sync_next(&b));
249 assert_se(barrier_sync_next(&b));
250 assert_se(barrier_sync(&b));
251 }),
252 TEST_BARRIER_WAIT_SUCCESS(pid1),
253 ({
254 set_alarm(BASE_TIME * 10);
255 sleep_for(BASE_TIME);
256 assert_se(barrier_place(&b));
257 assert_se(barrier_place(&b));
258 assert_se(barrier_sync(&b));
259 }),
260 TEST_BARRIER_WAIT_SUCCESS(pid2));
261
262 /*
263 * Test sync_next() and sync() with local barriers
264 * This tests timeouts if sync_*() is used if local barriers are placed but the
265 * remote didn't place any.
266 */
267 TEST_BARRIER(test_barrier_sync_next_local,
268 ({
269 set_alarm(BASE_TIME);
270 assert_se(barrier_place(&b));
271 assert_se(barrier_sync_next(&b));
272 assert_se(0);
273 }),
274 TEST_BARRIER_WAIT_ALARM(pid1),
275 ({
276 sleep_for(BASE_TIME * 2);
277 }),
278 TEST_BARRIER_WAIT_SUCCESS(pid2));
279
280 /*
281 * Test sync_next() and sync() with local barriers and abortion
282 * This is the same as test_barrier_sync_next_local but aborts the sync in the
283 * parent. Therefore, the sync_next() succeeds just fine due to the abortion.
284 */
285 TEST_BARRIER(test_barrier_sync_next_local_abort,
286 ({
287 set_alarm(BASE_TIME * 10);
288 assert_se(barrier_place(&b));
289 assert_se(!barrier_sync_next(&b));
290 }),
291 TEST_BARRIER_WAIT_SUCCESS(pid1),
292 ({
293 assert_se(barrier_abort(&b));
294 }),
295 TEST_BARRIER_WAIT_SUCCESS(pid2));
296
297 /*
298 * Test matched wait_abortion()
299 * This runs wait_abortion() with remote abortion.
300 */
301 TEST_BARRIER(test_barrier_wait_abortion,
302 ({
303 set_alarm(BASE_TIME * 10);
304 assert_se(barrier_wait_abortion(&b));
305 }),
306 TEST_BARRIER_WAIT_SUCCESS(pid1),
307 ({
308 assert_se(barrier_abort(&b));
309 }),
310 TEST_BARRIER_WAIT_SUCCESS(pid2));
311
312 /*
313 * Test unmatched wait_abortion()
314 * This runs wait_abortion() without any remote abortion going on. It thus must
315 * timeout.
316 */
317 TEST_BARRIER(test_barrier_wait_abortion_unmatched,
318 ({
319 set_alarm(BASE_TIME);
320 assert_se(barrier_wait_abortion(&b));
321 assert_se(0);
322 }),
323 TEST_BARRIER_WAIT_ALARM(pid1),
324 ({
325 sleep_for(BASE_TIME * 2);
326 }),
327 TEST_BARRIER_WAIT_SUCCESS(pid2));
328
329 /*
330 * Test matched wait_abortion() with local abortion
331 * This runs wait_abortion() with local and remote abortion.
332 */
333 TEST_BARRIER(test_barrier_wait_abortion_local,
334 ({
335 set_alarm(BASE_TIME * 10);
336 assert_se(barrier_abort(&b));
337 assert_se(!barrier_wait_abortion(&b));
338 }),
339 TEST_BARRIER_WAIT_SUCCESS(pid1),
340 ({
341 assert_se(barrier_abort(&b));
342 }),
343 TEST_BARRIER_WAIT_SUCCESS(pid2));
344
345 /*
346 * Test unmatched wait_abortion() with local abortion
347 * This runs wait_abortion() with only local abortion. This must time out.
348 */
349 TEST_BARRIER(test_barrier_wait_abortion_local_unmatched,
350 ({
351 set_alarm(BASE_TIME);
352 assert_se(barrier_abort(&b));
353 assert_se(!barrier_wait_abortion(&b));
354 assert_se(0);
355 }),
356 TEST_BARRIER_WAIT_ALARM(pid1),
357 ({
358 sleep_for(BASE_TIME * 2);
359 }),
360 TEST_BARRIER_WAIT_SUCCESS(pid2));
361
362 /*
363 * Test child exit
364 * Place barrier and sync with the child. The child only exits()s, which should
365 * cause an implicit abortion and wake the parent.
366 */
367 TEST_BARRIER(test_barrier_exit,
368 ({
369 }),
370 TEST_BARRIER_WAIT_SUCCESS(pid1),
371 ({
372 set_alarm(BASE_TIME * 10);
373 assert_se(barrier_place(&b));
374 assert_se(!barrier_sync(&b));
375 }),
376 TEST_BARRIER_WAIT_SUCCESS(pid2));
377
378 /*
379 * Test child exit with sleep
380 * Same as test_barrier_exit but verifies the test really works due to the
381 * child-exit. We add a usleep() which triggers the alarm in the parent and
382 * causes the test to time out.
383 */
384 TEST_BARRIER(test_barrier_no_exit,
385 ({
386 sleep_for(BASE_TIME * 2);
387 }),
388 TEST_BARRIER_WAIT_SUCCESS(pid1),
389 ({
390 set_alarm(BASE_TIME);
391 assert_se(barrier_place(&b));
392 assert_se(!barrier_sync(&b));
393 }),
394 TEST_BARRIER_WAIT_ALARM(pid2));
395
396 /*
397 * Test pending exit against sync
398 * The parent places a barrier *and* exits. The 20ms wait in the child
399 * guarantees both are pending. However, our logic prefers pending barriers over
400 * pending exit-abortions (unlike normal abortions), thus the wait_next() must
401 * succeed, same for the sync_next() as our local barrier-count is smaller than
402 * the remote. Once we place a barrier our count is equal, so the sync still
403 * succeeds. Only if we place one more barrier, we're ahead of the remote, thus
404 * we will fail due to HUP on the pipe.
405 */
406 TEST_BARRIER(test_barrier_pending_exit,
407 ({
408 set_alarm(BASE_TIME * 4);
409 sleep_for(BASE_TIME * 2);
410 assert_se(barrier_wait_next(&b));
411 assert_se(barrier_sync_next(&b));
412 assert_se(barrier_place(&b));
413 assert_se(barrier_sync_next(&b));
414 assert_se(barrier_place(&b));
415 assert_se(!barrier_sync_next(&b));
416 }),
417 TEST_BARRIER_WAIT_SUCCESS(pid1),
418 ({
419 assert_se(barrier_place(&b));
420 }),
421 TEST_BARRIER_WAIT_SUCCESS(pid2));
422
423 int main(int argc, char *argv[]) {
424 int v;
425 test_setup_logging(LOG_INFO);
426
427 if (!slow_tests_enabled())
428 return log_tests_skipped("slow tests are disabled");
429
430 /*
431 * This test uses real-time alarms and sleeps to test for CPU races
432 * explicitly. This is highly fragile if your system is under load. We
433 * already increased the BASE_TIME value to make the tests more robust,
434 * but that just makes the test take significantly longer. Given the recent
435 * issues when running the test in a virtualized environments, limit it
436 * to bare metal machines only, to minimize false-positives in CIs.
437 */
438 v = detect_virtualization();
439 if (IN_SET(v, -EPERM, -EACCES))
440 return log_tests_skipped("Cannot detect virtualization");
441
442 if (v != VIRTUALIZATION_NONE)
443 return log_tests_skipped("This test requires a baremetal machine");
444
445 test_barrier_sync();
446 test_barrier_wait_next();
447 test_barrier_wait_next_twice();
448 test_barrier_wait_next_twice_sync();
449 test_barrier_wait_next_twice_local();
450 test_barrier_wait_next_twice_local_sync();
451 test_barrier_sync_next();
452 test_barrier_sync_next_local();
453 test_barrier_sync_next_local_abort();
454 test_barrier_wait_abortion();
455 test_barrier_wait_abortion_unmatched();
456 test_barrier_wait_abortion_local();
457 test_barrier_wait_abortion_local_unmatched();
458 test_barrier_exit();
459 test_barrier_no_exit();
460 test_barrier_pending_exit();
461
462 return 0;
463 }