1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: Test IORING_ASYNC_CANCEL_{ALL,FD}
4 *
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <poll.h>
12
13 #include "liburing.h"
14
15 static int no_cancel_flags;
16
test1(struct io_uring * ring,int * fd,int fixed)17 static int test1(struct io_uring *ring, int *fd, int fixed)
18 {
19 struct io_uring_sqe *sqe;
20 struct io_uring_cqe *cqe;
21 int ret, i, __fd = fd[0];
22
23 if (fixed)
24 __fd = 0;
25
26 if (fixed) {
27 ret = io_uring_register_files(ring, fd, 1);
28 if (ret) {
29 fprintf(stderr, "failed file register %d\n", ret);
30 return 1;
31 }
32 }
33
34 for (i = 0; i < 8; i++) {
35 sqe = io_uring_get_sqe(ring);
36 if (!sqe) {
37 fprintf(stderr, "get sqe failed\n");
38 return 1;
39 }
40
41 io_uring_prep_poll_add(sqe, __fd, POLLIN);
42 sqe->user_data = i + 1;
43 if (fixed)
44 sqe->flags |= IOSQE_FIXED_FILE;
45 }
46
47 ret = io_uring_submit(ring);
48 if (ret < 8) {
49 fprintf(stderr, "sqe submit failed: %d\n", ret);
50 return 1;
51 }
52
53 sqe = io_uring_get_sqe(ring);
54 if (!sqe) {
55 fprintf(stderr, "get sqe failed\n");
56 return 1;
57 }
58
59 /*
60 * Mark CANCEL_ALL to cancel all matching the key, and use
61 * CANCEL_FD to cancel requests matching the specified fd.
62 * This should cancel all the pending poll requests on the pipe
63 * input.
64 */
65 io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
66 sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
67 if (fixed)
68 sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD_FIXED;
69 sqe->fd = __fd;
70 sqe->user_data = 100;
71
72 ret = io_uring_submit(ring);
73 if (ret < 1) {
74 fprintf(stderr, "child: sqe submit failed: %d\n", ret);
75 return 1;
76 }
77
78 for (i = 0; i < 9; i++) {
79 if (no_cancel_flags)
80 break;
81 ret = io_uring_wait_cqe(ring, &cqe);
82 if (ret) {
83 fprintf(stderr, "wait=%d\n", ret);
84 return 1;
85 }
86 switch (cqe->user_data) {
87 case 100:
88 if (cqe->res == -EINVAL) {
89 no_cancel_flags = 1;
90 break;
91 }
92 if (cqe->res != 8) {
93 fprintf(stderr, "canceled %d\n", cqe->res);
94 return 1;
95 }
96 break;
97 case 1 ... 8:
98 if (cqe->res != -ECANCELED) {
99 fprintf(stderr, "poll res %d\n", cqe->res);
100 return 1;
101 }
102 break;
103 default:
104 fprintf(stderr, "invalid user_data %lu\n",
105 (unsigned long) cqe->user_data);
106 return 1;
107 }
108 io_uring_cqe_seen(ring, cqe);
109 }
110
111 if (fixed)
112 io_uring_unregister_files(ring);
113
114 return 0;
115 }
116
test2(struct io_uring * ring,int * fd)117 static int test2(struct io_uring *ring, int *fd)
118 {
119 struct io_uring_sqe *sqe;
120 struct io_uring_cqe *cqe;
121 int ret, i, fd2[2];
122
123 if (pipe(fd2) < 0) {
124 perror("pipe");
125 return 1;
126 }
127
128 for (i = 0; i < 8; i++) {
129 sqe = io_uring_get_sqe(ring);
130 if (!sqe) {
131 fprintf(stderr, "get sqe failed\n");
132 goto err;
133 }
134
135 if (!(i & 1))
136 io_uring_prep_poll_add(sqe, fd[0], POLLIN);
137 else
138 io_uring_prep_poll_add(sqe, fd2[0], POLLIN);
139 sqe->user_data = i & 1;
140 }
141
142 ret = io_uring_submit(ring);
143 if (ret < 8) {
144 fprintf(stderr, "sqe submit failed: %d\n", ret);
145 goto err;
146 }
147
148 sqe = io_uring_get_sqe(ring);
149 if (!sqe) {
150 fprintf(stderr, "get sqe failed\n");
151 goto err;
152 }
153
154 /*
155 * Mark CANCEL_ALL to cancel all matching the key, and use
156 * CANCEL_FD to cancel requests matching the specified fd.
157 * This should cancel all the pending poll requests on the pipe
158 * input.
159 */
160 io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
161 sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
162 sqe->fd = fd[0];
163 sqe->user_data = 100;
164
165 ret = io_uring_submit(ring);
166 if (ret < 1) {
167 fprintf(stderr, "sqe submit failed: %d\n", ret);
168 goto err;
169 }
170
171 for (i = 0; i < 5; i++) {
172 ret = io_uring_wait_cqe(ring, &cqe);
173 if (ret) {
174 fprintf(stderr, "wait=%d\n", ret);
175 goto err;
176 }
177 switch (cqe->user_data) {
178 case 100:
179 if (cqe->res != 4) {
180 fprintf(stderr, "canceled %d\n", cqe->res);
181 goto err;
182 }
183 break;
184 case 0:
185 if (cqe->res != -ECANCELED) {
186 fprintf(stderr, "poll res %d\n", cqe->res);
187 goto err;
188 }
189 break;
190 default:
191 fprintf(stderr, "invalid user_data %lu\n",
192 (unsigned long) cqe->user_data);
193 goto err;
194 }
195 io_uring_cqe_seen(ring, cqe);
196 }
197
198 usleep(1000);
199
200 /*
201 * Should not have any pending CQEs now
202 */
203 ret = io_uring_peek_cqe(ring, &cqe);
204 if (!ret) {
205 fprintf(stderr, "Unexpected extra cancel cqe\n");
206 goto err;
207 }
208
209 sqe = io_uring_get_sqe(ring);
210 if (!sqe) {
211 fprintf(stderr, "get sqe failed\n");
212 goto err;
213 }
214
215 /*
216 * Mark CANCEL_ALL to cancel all matching the key, and use
217 * CANCEL_FD to cancel requests matching the specified fd.
218 * This should cancel all the pending poll requests on the pipe
219 * input.
220 */
221 io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
222 sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
223 sqe->fd = fd2[0];
224 sqe->user_data = 100;
225
226 ret = io_uring_submit(ring);
227 if (ret < 1) {
228 fprintf(stderr, "sqe submit failed: %d\n", ret);
229 goto err;
230 }
231
232 for (i = 0; i < 5; i++) {
233 ret = io_uring_wait_cqe(ring, &cqe);
234 if (ret) {
235 fprintf(stderr, "wait=%d\n", ret);
236 goto err;
237 }
238 switch (cqe->user_data) {
239 case 100:
240 if (cqe->res != 4) {
241 fprintf(stderr, "canceled %d\n", cqe->res);
242 goto err;
243 }
244 break;
245 case 1:
246 if (cqe->res != -ECANCELED) {
247 fprintf(stderr, "poll res %d\n", cqe->res);
248 goto err;
249 }
250 break;
251 default:
252 fprintf(stderr, "invalid user_data %lu\n",
253 (unsigned long) cqe->user_data);
254 goto err;
255 }
256 io_uring_cqe_seen(ring, cqe);
257 }
258
259 close(fd2[0]);
260 close(fd2[1]);
261 return 0;
262 err:
263 close(fd2[0]);
264 close(fd2[1]);
265 return 1;
266 }
267
test3(struct io_uring * ring,int * fd)268 static int test3(struct io_uring *ring, int *fd)
269 {
270 struct io_uring_sqe *sqe;
271 struct io_uring_cqe *cqe;
272 int ret, i, fd2[2];
273
274 if (pipe(fd2) < 0) {
275 perror("pipe");
276 return 1;
277 }
278
279 for (i = 0; i < 8; i++) {
280 sqe = io_uring_get_sqe(ring);
281 if (!sqe) {
282 fprintf(stderr, "get sqe failed\n");
283 goto err;
284 }
285
286 if (!(i & 1)) {
287 io_uring_prep_poll_add(sqe, fd[0], POLLIN);
288 sqe->flags |= IOSQE_ASYNC;
289 } else
290 io_uring_prep_poll_add(sqe, fd2[0], POLLIN);
291 sqe->user_data = i & 1;
292 }
293
294 ret = io_uring_submit(ring);
295 if (ret < 8) {
296 fprintf(stderr, "child: sqe submit failed: %d\n", ret);
297 goto err;
298 }
299
300 usleep(10000);
301
302 sqe = io_uring_get_sqe(ring);
303 if (!sqe) {
304 fprintf(stderr, "get sqe failed\n");
305 goto err;
306 }
307
308 /*
309 * Mark CANCEL_ALL to cancel all matching the key, and use
310 * CANCEL_FD to cancel requests matching the specified fd.
311 * This should cancel all the pending poll requests on the pipe
312 * input.
313 */
314 io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
315 sqe->cancel_flags |= IORING_ASYNC_CANCEL_ANY;
316 sqe->fd = 0;
317 sqe->user_data = 100;
318
319 ret = io_uring_submit(ring);
320 if (ret < 1) {
321 fprintf(stderr, "child: sqe submit failed: %d\n", ret);
322 goto err;
323 }
324
325 for (i = 0; i < 9; i++) {
326 ret = io_uring_wait_cqe(ring, &cqe);
327 if (ret) {
328 fprintf(stderr, "wait=%d\n", ret);
329 goto err;
330 }
331 switch (cqe->user_data) {
332 case 100:
333 if (cqe->res != 8) {
334 fprintf(stderr, "canceled %d\n", cqe->res);
335 goto err;
336 }
337 break;
338 case 0:
339 case 1:
340 if (cqe->res != -ECANCELED) {
341 fprintf(stderr, "poll res %d\n", cqe->res);
342 goto err;
343 }
344 break;
345 default:
346 fprintf(stderr, "invalid user_data %lu\n",
347 (unsigned long) cqe->user_data);
348 goto err;
349 }
350 io_uring_cqe_seen(ring, cqe);
351 }
352
353 close(fd2[0]);
354 close(fd2[1]);
355 return 0;
356 err:
357 close(fd2[0]);
358 close(fd2[1]);
359 return 1;
360 }
361
test4(struct io_uring * ring,int * fd)362 static int test4(struct io_uring *ring, int *fd)
363 {
364 struct io_uring_sqe *sqe;
365 struct io_uring_cqe *cqe;
366 char buffer[32];
367 int ret, i;
368
369 for (i = 0; i < 8; i++) {
370 sqe = io_uring_get_sqe(ring);
371 if (!sqe) {
372 fprintf(stderr, "get sqe failed\n");
373 goto err;
374 }
375
376 io_uring_prep_read(sqe, fd[0], &buffer, sizeof(buffer), 0);
377 sqe->flags |= IOSQE_ASYNC;
378 sqe->user_data = i + 1;
379 }
380
381 ret = io_uring_submit(ring);
382 if (ret < 8) {
383 fprintf(stderr, "child: sqe submit failed: %d\n", ret);
384 goto err;
385 }
386
387 usleep(10000);
388
389 sqe = io_uring_get_sqe(ring);
390 if (!sqe) {
391 fprintf(stderr, "get sqe failed\n");
392 goto err;
393 }
394
395 /*
396 * Mark CANCEL_ALL to cancel all matching the key, and use
397 * CANCEL_FD to cancel requests matching the specified fd.
398 * This should cancel all the pending poll requests on the pipe
399 * input.
400 */
401 io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
402 sqe->cancel_flags |= IORING_ASYNC_CANCEL_ANY;
403 sqe->fd = 0;
404 sqe->user_data = 100;
405
406 ret = io_uring_submit(ring);
407 if (ret < 1) {
408 fprintf(stderr, "child: sqe submit failed: %d\n", ret);
409 goto err;
410 }
411
412 for (i = 0; i < 9; i++) {
413 ret = io_uring_wait_cqe(ring, &cqe);
414 if (ret) {
415 fprintf(stderr, "wait=%d\n", ret);
416 goto err;
417 }
418 switch (cqe->user_data) {
419 case 100:
420 if (cqe->res != 8) {
421 fprintf(stderr, "canceled %d\n", cqe->res);
422 goto err;
423 }
424 break;
425 case 1 ... 8:
426 if (cqe->res != -ECANCELED) {
427 fprintf(stderr, "poll res %d\n", cqe->res);
428 goto err;
429 }
430 break;
431 default:
432 fprintf(stderr, "invalid user_data %lu\n",
433 (unsigned long) cqe->user_data);
434 goto err;
435 }
436 io_uring_cqe_seen(ring, cqe);
437 }
438
439 return 0;
440 err:
441 return 1;
442 }
443
main(int argc,char * argv[])444 int main(int argc, char *argv[])
445 {
446 struct io_uring ring;
447 int ret, fd[2];
448
449 if (argc > 1)
450 return 0;
451
452 if (pipe(fd) < 0) {
453 perror("pipe");
454 return 1;
455 }
456
457 ret = io_uring_queue_init(8, &ring, 0);
458 if (ret) {
459 fprintf(stderr, "ring setup failed: %d\n", ret);
460 return 1;
461 }
462
463 ret = test1(&ring, fd, 0);
464 if (ret) {
465 fprintf(stderr, "test1 failed\n");
466 return ret;
467 }
468 if (no_cancel_flags)
469 return 0;
470
471 ret = test1(&ring, fd, 1);
472 if (ret) {
473 fprintf(stderr, "test1 fixed failed\n");
474 return ret;
475 }
476
477 ret = test2(&ring, fd);
478 if (ret) {
479 fprintf(stderr, "test2 failed\n");
480 return ret;
481 }
482
483 ret = test3(&ring, fd);
484 if (ret) {
485 fprintf(stderr, "test3 failed\n");
486 return ret;
487 }
488
489 ret = test4(&ring, fd);
490 if (ret) {
491 fprintf(stderr, "test4 failed\n");
492 return ret;
493 }
494
495 return 0;
496 }
497