1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test waitid functionality
4 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <string.h>
9
10 #include "liburing.h"
11 #include "helpers.h"
12
13 static bool no_waitid;
14
child(long usleep_time)15 static void child(long usleep_time)
16 {
17 if (usleep_time)
18 usleep(usleep_time);
19 exit(0);
20 }
21
test_invalid_infop(struct io_uring * ring)22 static int test_invalid_infop(struct io_uring *ring)
23 {
24 struct io_uring_sqe *sqe;
25 struct io_uring_cqe *cqe;
26 siginfo_t *si = (siginfo_t *) (uintptr_t) 0x1234;
27 int ret, w;
28 pid_t pid;
29
30 pid = fork();
31 if (!pid) {
32 child(200000);
33 exit(0);
34 }
35
36 sqe = io_uring_get_sqe(ring);
37 io_uring_prep_waitid(sqe, P_PID, pid, si, WEXITED, 0);
38 sqe->user_data = 1;
39 io_uring_submit(ring);
40
41 ret = io_uring_wait_cqe(ring, &cqe);
42 if (ret) {
43 fprintf(stderr, "cqe wait: %d\n", ret);
44 return T_EXIT_FAIL;
45 }
46 if (cqe->res != -EFAULT) {
47 fprintf(stderr, "Bad return on invalid infop: %d\n", cqe->res);
48 return T_EXIT_FAIL;
49 }
50 io_uring_cqe_seen(ring, cqe);
51 wait(&w);
52 return T_EXIT_PASS;
53 }
54
55 /*
56 * Test linked timeout with child not exiting in time
57 */
test_noexit(struct io_uring * ring)58 static int test_noexit(struct io_uring *ring)
59 {
60 struct io_uring_sqe *sqe;
61 struct io_uring_cqe *cqe;
62 struct __kernel_timespec ts;
63 int ret, i, w;
64 siginfo_t si;
65 pid_t pid;
66
67 pid = fork();
68 if (!pid) {
69 child(200000);
70 exit(0);
71 }
72
73 sqe = io_uring_get_sqe(ring);
74 io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0);
75 sqe->flags |= IOSQE_IO_LINK;
76 sqe->user_data = 1;
77
78 ts.tv_sec = 0;
79 ts.tv_nsec = 100 * 1000 * 1000ULL;
80 sqe = io_uring_get_sqe(ring);
81 io_uring_prep_link_timeout(sqe, &ts, 0);
82 sqe->user_data = 2;
83
84 io_uring_submit(ring);
85
86 for (i = 0; i < 2; i++) {
87 ret = io_uring_wait_cqe(ring, &cqe);
88 if (ret) {
89 fprintf(stderr, "cqe wait: %d\n", ret);
90 return T_EXIT_FAIL;
91 }
92 if (cqe->user_data == 2 && cqe->res != 1) {
93 fprintf(stderr, "timeout res: %d\n", cqe->res);
94 return T_EXIT_FAIL;
95 }
96 if (cqe->user_data == 1 && cqe->res != -ECANCELED) {
97 fprintf(stderr, "waitid res: %d\n", cqe->res);
98 return T_EXIT_FAIL;
99 }
100 io_uring_cqe_seen(ring, cqe);
101 }
102
103 wait(&w);
104 return T_EXIT_PASS;
105 }
106
107 /*
108 * Test one child exiting, but not the one we were looking for
109 */
test_double(struct io_uring * ring)110 static int test_double(struct io_uring *ring)
111 {
112 struct io_uring_sqe *sqe;
113 struct io_uring_cqe *cqe;
114 siginfo_t si;
115 pid_t p1, p2;
116 int ret, w;
117
118 /* p1 will exit shortly */
119 p1 = fork();
120 if (!p1) {
121 child(100000);
122 exit(0);
123 }
124
125 /* p2 will linger */
126 p2 = fork();
127 if (!p2) {
128 child(200000);
129 exit(0);
130 }
131
132 sqe = io_uring_get_sqe(ring);
133 io_uring_prep_waitid(sqe, P_PID, p2, &si, WEXITED, 0);
134
135 io_uring_submit(ring);
136
137 ret = io_uring_wait_cqe(ring, &cqe);
138 if (ret) {
139 fprintf(stderr, "cqe wait: %d\n", ret);
140 return T_EXIT_FAIL;
141 }
142
143 if (cqe->res < 0) {
144 fprintf(stderr, "cqe res: %d\n", cqe->res);
145 return T_EXIT_FAIL;
146 }
147 if (si.si_pid != p2) {
148 fprintf(stderr, "expected pid %d, got %d\n", p2, si.si_pid);
149 return T_EXIT_FAIL;
150 }
151
152 io_uring_cqe_seen(ring, cqe);
153 wait(&w);
154 return T_EXIT_PASS;
155 }
156
157 /*
158 * Test reaping of an already exited task
159 */
test_ready(struct io_uring * ring)160 static int test_ready(struct io_uring *ring)
161 {
162 struct io_uring_sqe *sqe;
163 struct io_uring_cqe *cqe;
164 siginfo_t si;
165 pid_t pid;
166 int ret;
167
168 pid = fork();
169 if (!pid) {
170 child(0);
171 exit(0);
172 }
173
174 sqe = io_uring_get_sqe(ring);
175 io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0);
176
177 io_uring_submit(ring);
178
179 ret = io_uring_wait_cqe(ring, &cqe);
180 if (ret) {
181 fprintf(stderr, "cqe wait: %d\n", ret);
182 return T_EXIT_FAIL;
183 }
184
185 if (cqe->res < 0) {
186 fprintf(stderr, "cqe res: %d\n", cqe->res);
187 return T_EXIT_FAIL;
188 }
189 if (si.si_pid != pid) {
190 fprintf(stderr, "expected pid %d, got %d\n", pid, si.si_pid);
191 return T_EXIT_FAIL;
192 }
193
194 io_uring_cqe_seen(ring, cqe);
195 return T_EXIT_PASS;
196 }
197
198 /*
199 * Test cancelation of pending waitid
200 */
test_cancel(struct io_uring * ring)201 static int test_cancel(struct io_uring *ring)
202 {
203 struct io_uring_sqe *sqe;
204 struct io_uring_cqe *cqe;
205 int ret, i, w;
206 pid_t pid;
207
208 pid = fork();
209 if (!pid) {
210 child(20000);
211 exit(0);
212 }
213
214 sqe = io_uring_get_sqe(ring);
215 io_uring_prep_waitid(sqe, P_PID, pid, NULL, WEXITED, 0);
216 sqe->user_data = 1;
217
218 io_uring_submit(ring);
219
220 sqe = io_uring_get_sqe(ring);
221 io_uring_prep_cancel64(sqe, 1, 0);
222 sqe->user_data = 2;
223
224 io_uring_submit(ring);
225
226 for (i = 0; i < 2; i++) {
227 ret = io_uring_wait_cqe(ring, &cqe);
228 if (ret) {
229 fprintf(stderr, "cqe wait: %d\n", ret);
230 return T_EXIT_FAIL;
231 }
232 if (cqe->user_data == 1 && cqe->res != -ECANCELED) {
233 fprintf(stderr, "cqe res: %d\n", cqe->res);
234 return T_EXIT_FAIL;
235 }
236 if (cqe->user_data == 2 && cqe->res != 1) {
237 fprintf(stderr, "cqe res: %d\n", cqe->res);
238 return T_EXIT_FAIL;
239 }
240 io_uring_cqe_seen(ring, cqe);
241 }
242
243 wait(&w);
244 return T_EXIT_PASS;
245 }
246
247 /*
248 * Test cancelation of pending waitid, with expected races that either
249 * waitid trigger or cancelation will win.
250 */
test_cancel_race(struct io_uring * ring,int async)251 static int test_cancel_race(struct io_uring *ring, int async)
252 {
253 struct io_uring_sqe *sqe;
254 struct io_uring_cqe *cqe;
255 int ret, i, to_wait, total_forks;
256 pid_t pid;
257
258 total_forks = 0;
259 for (i = 0; i < 10; i++) {
260 total_forks++;
261 pid = fork();
262 if (!pid) {
263 child(getpid() & 1);
264 exit(0);
265 }
266 }
267
268 sqe = io_uring_get_sqe(ring);
269 io_uring_prep_waitid(sqe, P_ALL, -1, NULL, WEXITED, 0);
270 if (async)
271 sqe->flags |= IOSQE_ASYNC;
272 sqe->user_data = 1;
273
274 io_uring_submit(ring);
275
276 sqe = io_uring_get_sqe(ring);
277 io_uring_prep_cancel64(sqe, 1, 0);
278 sqe->user_data = 2;
279
280 usleep(1);
281
282 io_uring_submit(ring);
283
284 to_wait = total_forks;
285 for (i = 0; i < 2; i++) {
286 ret = io_uring_wait_cqe(ring, &cqe);
287 if (ret) {
288 fprintf(stderr, "cqe wait: %d\n", ret);
289 return T_EXIT_FAIL;
290 }
291 if (cqe->user_data == 1) {
292 if (!cqe->res)
293 to_wait--;
294 if (!(cqe->res == -ECANCELED || cqe->res == 0)) {
295 fprintf(stderr, "cqe1 res: %d\n", cqe->res);
296 return T_EXIT_FAIL;
297 }
298 }
299 if (cqe->user_data == 2 &&
300 !(cqe->res == 1 || cqe->res == 0 || cqe->res == -ENOENT ||
301 cqe->res == -EALREADY)) {
302 fprintf(stderr, "cqe2 res: %d\n", cqe->res);
303 return T_EXIT_FAIL;
304 }
305 io_uring_cqe_seen(ring, cqe);
306 }
307
308 for (i = 0; i < to_wait; i++) {
309 int w;
310
311 wait(&w);
312 }
313
314 return T_EXIT_PASS;
315 }
316
317 /*
318 * Test basic reap of child exit
319 */
test(struct io_uring * ring)320 static int test(struct io_uring *ring)
321 {
322 struct io_uring_sqe *sqe;
323 struct io_uring_cqe *cqe;
324 siginfo_t si;
325 pid_t pid;
326 int ret;
327
328 pid = fork();
329 if (!pid) {
330 child(100);
331 exit(0);
332 }
333
334 sqe = io_uring_get_sqe(ring);
335 io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0);
336
337 io_uring_submit(ring);
338
339 ret = io_uring_wait_cqe(ring, &cqe);
340 if (ret) {
341 fprintf(stderr, "cqe wait: %d\n", ret);
342 return T_EXIT_FAIL;
343 }
344
345 /* no waitid support */
346 if (cqe->res == -EINVAL) {
347 no_waitid = true;
348 return T_EXIT_SKIP;
349 }
350 if (cqe->res < 0) {
351 fprintf(stderr, "cqe res: %d\n", cqe->res);
352 return T_EXIT_FAIL;
353 }
354 if (si.si_pid != pid) {
355 fprintf(stderr, "expected pid %d, got %d\n", pid, si.si_pid);
356 return T_EXIT_FAIL;
357 }
358
359 io_uring_cqe_seen(ring, cqe);
360 return T_EXIT_PASS;
361 }
362
main(int argc,char * argv[])363 int main(int argc, char *argv[])
364 {
365 struct io_uring ring;
366 int ret, i;
367
368 if (argc > 1)
369 return T_EXIT_SKIP;
370
371 io_uring_queue_init(8, &ring, 0);
372
373 ret = test(&ring);
374 if (ret == T_EXIT_FAIL) {
375 fprintf(stderr, "test failed\n");
376 return T_EXIT_FAIL;
377 }
378 if (no_waitid)
379 return T_EXIT_SKIP;
380
381 ret = test_noexit(&ring);
382 if (ret == T_EXIT_FAIL) {
383 fprintf(stderr, "test_noexit failed\n");
384 return T_EXIT_FAIL;
385 }
386
387 ret = test_noexit(&ring);
388 if (ret == T_EXIT_FAIL) {
389 fprintf(stderr, "test_noexit failed\n");
390 return T_EXIT_FAIL;
391 }
392
393 ret = test_double(&ring);
394 if (ret == T_EXIT_FAIL) {
395 fprintf(stderr, "test_double failed\n");
396 return T_EXIT_FAIL;
397 }
398
399 ret = test_ready(&ring);
400 if (ret == T_EXIT_FAIL) {
401 fprintf(stderr, "test_ready failed\n");
402 return T_EXIT_FAIL;
403 }
404
405 ret = test_cancel(&ring);
406 if (ret == T_EXIT_FAIL) {
407 fprintf(stderr, "test_cancel failed\n");
408 return T_EXIT_FAIL;
409 }
410
411 ret = test_invalid_infop(&ring);
412 if (ret == T_EXIT_FAIL) {
413 fprintf(stderr, "test_invalid_infop failed\n");
414 return T_EXIT_FAIL;
415 }
416
417 for (i = 0; i < 1000; i++) {
418 ret = test_cancel_race(&ring, i & 1);
419 if (ret == T_EXIT_FAIL) {
420 fprintf(stderr, "test_cancel_race failed\n");
421 return T_EXIT_FAIL;
422 }
423 }
424
425 io_uring_queue_exit(&ring);
426 return T_EXIT_PASS;
427 }
428