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