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