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