• 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 
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