• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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