1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: run various linked sqe tests
4 *
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12
13 #include "liburing.h"
14
15 static int no_hardlink;
16
17 /*
18 * Timer with single nop
19 */
test_single_hardlink(struct io_uring * ring)20 static int test_single_hardlink(struct io_uring *ring)
21 {
22 struct __kernel_timespec ts;
23 struct io_uring_cqe *cqe;
24 struct io_uring_sqe *sqe;
25 int ret, i;
26
27 sqe = io_uring_get_sqe(ring);
28 if (!sqe) {
29 fprintf(stderr, "get sqe failed\n");
30 goto err;
31 }
32 ts.tv_sec = 0;
33 ts.tv_nsec = 10000000ULL;
34 io_uring_prep_timeout(sqe, &ts, 0, 0);
35 sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
36 sqe->user_data = 1;
37
38 sqe = io_uring_get_sqe(ring);
39 if (!sqe) {
40 fprintf(stderr, "get sqe failed\n");
41 goto err;
42 }
43 io_uring_prep_nop(sqe);
44 sqe->user_data = 2;
45
46 ret = io_uring_submit(ring);
47 if (ret <= 0) {
48 fprintf(stderr, "sqe submit failed: %d\n", ret);
49 goto err;
50 }
51
52 for (i = 0; i < 2; i++) {
53 ret = io_uring_wait_cqe(ring, &cqe);
54 if (ret < 0) {
55 fprintf(stderr, "wait completion %d\n", ret);
56 goto err;
57 }
58 if (!cqe) {
59 fprintf(stderr, "failed to get cqe\n");
60 goto err;
61 }
62 if (no_hardlink)
63 goto next;
64 if (cqe->user_data == 1 && cqe->res == -EINVAL) {
65 fprintf(stdout, "Hard links not supported, skipping\n");
66 no_hardlink = 1;
67 goto next;
68 }
69 if (cqe->user_data == 1 && cqe->res != -ETIME) {
70 fprintf(stderr, "timeout failed with %d\n", cqe->res);
71 goto err;
72 }
73 if (cqe->user_data == 2 && cqe->res) {
74 fprintf(stderr, "nop failed with %d\n", cqe->res);
75 goto err;
76 }
77 next:
78 io_uring_cqe_seen(ring, cqe);
79 }
80
81 return 0;
82 err:
83 return 1;
84 }
85
86 /*
87 * Timer -> timer -> nop
88 */
test_double_hardlink(struct io_uring * ring)89 static int test_double_hardlink(struct io_uring *ring)
90 {
91 struct __kernel_timespec ts1, ts2;
92 struct io_uring_cqe *cqe;
93 struct io_uring_sqe *sqe;
94 int ret, i;
95
96 if (no_hardlink)
97 return 0;
98
99 sqe = io_uring_get_sqe(ring);
100 if (!sqe) {
101 fprintf(stderr, "get sqe failed\n");
102 goto err;
103 }
104 ts1.tv_sec = 0;
105 ts1.tv_nsec = 10000000ULL;
106 io_uring_prep_timeout(sqe, &ts1, 0, 0);
107 sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
108 sqe->user_data = 1;
109
110 sqe = io_uring_get_sqe(ring);
111 if (!sqe) {
112 fprintf(stderr, "get sqe failed\n");
113 goto err;
114 }
115 ts2.tv_sec = 0;
116 ts2.tv_nsec = 15000000ULL;
117 io_uring_prep_timeout(sqe, &ts2, 0, 0);
118 sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
119 sqe->user_data = 2;
120
121 sqe = io_uring_get_sqe(ring);
122 if (!sqe) {
123 fprintf(stderr, "get sqe failed\n");
124 goto err;
125 }
126 io_uring_prep_nop(sqe);
127 sqe->user_data = 3;
128
129 ret = io_uring_submit(ring);
130 if (ret <= 0) {
131 fprintf(stderr, "sqe submit failed: %d\n", ret);
132 goto err;
133 }
134
135 for (i = 0; i < 3; i++) {
136 ret = io_uring_wait_cqe(ring, &cqe);
137 if (ret < 0) {
138 fprintf(stderr, "wait completion %d\n", ret);
139 goto err;
140 }
141 if (!cqe) {
142 fprintf(stderr, "failed to get cqe\n");
143 goto err;
144 }
145 if (cqe->user_data == 1 && cqe->res != -ETIME) {
146 fprintf(stderr, "timeout failed with %d\n", cqe->res);
147 goto err;
148 }
149 if (cqe->user_data == 2 && cqe->res != -ETIME) {
150 fprintf(stderr, "timeout failed with %d\n", cqe->res);
151 goto err;
152 }
153 if (cqe->user_data == 3 && cqe->res) {
154 fprintf(stderr, "nop failed with %d\n", cqe->res);
155 goto err;
156 }
157 io_uring_cqe_seen(ring, cqe);
158 }
159
160 return 0;
161 err:
162 return 1;
163
164 }
165
166 /*
167 * Test failing head of chain, and dependent getting -ECANCELED
168 */
test_single_link_fail(struct io_uring * ring)169 static int test_single_link_fail(struct io_uring *ring)
170 {
171 struct io_uring_cqe *cqe;
172 struct io_uring_sqe *sqe;
173 int ret, i;
174
175 sqe = io_uring_get_sqe(ring);
176 if (!sqe) {
177 printf("get sqe failed\n");
178 goto err;
179 }
180
181 io_uring_prep_remove_buffers(sqe, 10, 1);
182 sqe->flags |= IOSQE_IO_LINK;
183
184 sqe = io_uring_get_sqe(ring);
185 if (!sqe) {
186 printf("get sqe failed\n");
187 goto err;
188 }
189
190 io_uring_prep_nop(sqe);
191
192 ret = io_uring_submit(ring);
193 if (ret <= 0) {
194 printf("sqe submit failed: %d\n", ret);
195 goto err;
196 }
197
198 for (i = 0; i < 2; i++) {
199 ret = io_uring_peek_cqe(ring, &cqe);
200 if (ret < 0) {
201 printf("wait completion %d\n", ret);
202 goto err;
203 }
204 if (!cqe) {
205 printf("failed to get cqe\n");
206 goto err;
207 }
208 if (i == 0 && cqe->res != -ENOENT) {
209 printf("sqe0 failed with %d, wanted -ENOENT\n", cqe->res);
210 goto err;
211 }
212 if (i == 1 && cqe->res != -ECANCELED) {
213 printf("sqe1 failed with %d, wanted -ECANCELED\n", cqe->res);
214 goto err;
215 }
216 io_uring_cqe_seen(ring, cqe);
217 }
218
219 return 0;
220 err:
221 return 1;
222 }
223
224 /*
225 * Test two independent chains
226 */
test_double_chain(struct io_uring * ring)227 static int test_double_chain(struct io_uring *ring)
228 {
229 struct io_uring_cqe *cqe;
230 struct io_uring_sqe *sqe;
231 int ret, i;
232
233 sqe = io_uring_get_sqe(ring);
234 if (!sqe) {
235 printf("get sqe failed\n");
236 goto err;
237 }
238
239 io_uring_prep_nop(sqe);
240 sqe->flags |= IOSQE_IO_LINK;
241
242 sqe = io_uring_get_sqe(ring);
243 if (!sqe) {
244 printf("get sqe failed\n");
245 goto err;
246 }
247
248 io_uring_prep_nop(sqe);
249
250 sqe = io_uring_get_sqe(ring);
251 if (!sqe) {
252 printf("get sqe failed\n");
253 goto err;
254 }
255
256 io_uring_prep_nop(sqe);
257 sqe->flags |= IOSQE_IO_LINK;
258
259 sqe = io_uring_get_sqe(ring);
260 if (!sqe) {
261 printf("get sqe failed\n");
262 goto err;
263 }
264
265 io_uring_prep_nop(sqe);
266
267 ret = io_uring_submit(ring);
268 if (ret <= 0) {
269 printf("sqe submit failed: %d\n", ret);
270 goto err;
271 }
272
273 for (i = 0; i < 4; i++) {
274 ret = io_uring_wait_cqe(ring, &cqe);
275 if (ret < 0) {
276 printf("wait completion %d\n", ret);
277 goto err;
278 }
279 io_uring_cqe_seen(ring, cqe);
280 }
281
282 return 0;
283 err:
284 return 1;
285 }
286
287 /*
288 * Test multiple dependents
289 */
test_double_link(struct io_uring * ring)290 static int test_double_link(struct io_uring *ring)
291 {
292 struct io_uring_cqe *cqe;
293 struct io_uring_sqe *sqe;
294 int ret, i;
295
296 sqe = io_uring_get_sqe(ring);
297 if (!sqe) {
298 printf("get sqe failed\n");
299 goto err;
300 }
301
302 io_uring_prep_nop(sqe);
303 sqe->flags |= IOSQE_IO_LINK;
304
305 sqe = io_uring_get_sqe(ring);
306 if (!sqe) {
307 printf("get sqe failed\n");
308 goto err;
309 }
310
311 io_uring_prep_nop(sqe);
312 sqe->flags |= IOSQE_IO_LINK;
313
314 sqe = io_uring_get_sqe(ring);
315 if (!sqe) {
316 printf("get sqe failed\n");
317 goto err;
318 }
319
320 io_uring_prep_nop(sqe);
321
322 ret = io_uring_submit(ring);
323 if (ret <= 0) {
324 printf("sqe submit failed: %d\n", ret);
325 goto err;
326 }
327
328 for (i = 0; i < 3; i++) {
329 ret = io_uring_wait_cqe(ring, &cqe);
330 if (ret < 0) {
331 printf("wait completion %d\n", ret);
332 goto err;
333 }
334 io_uring_cqe_seen(ring, cqe);
335 }
336
337 return 0;
338 err:
339 return 1;
340 }
341
342 /*
343 * Test single dependency
344 */
test_single_link(struct io_uring * ring)345 static int test_single_link(struct io_uring *ring)
346 {
347 struct io_uring_cqe *cqe;
348 struct io_uring_sqe *sqe;
349 int ret, i;
350
351 sqe = io_uring_get_sqe(ring);
352 if (!sqe) {
353 printf("get sqe failed\n");
354 goto err;
355 }
356
357 io_uring_prep_nop(sqe);
358 sqe->flags |= IOSQE_IO_LINK;
359
360 sqe = io_uring_get_sqe(ring);
361 if (!sqe) {
362 printf("get sqe failed\n");
363 goto err;
364 }
365
366 io_uring_prep_nop(sqe);
367
368 ret = io_uring_submit(ring);
369 if (ret <= 0) {
370 printf("sqe submit failed: %d\n", ret);
371 goto err;
372 }
373
374 for (i = 0; i < 2; i++) {
375 ret = io_uring_wait_cqe(ring, &cqe);
376 if (ret < 0) {
377 printf("wait completion %d\n", ret);
378 goto err;
379 }
380 io_uring_cqe_seen(ring, cqe);
381 }
382
383 return 0;
384 err:
385 return 1;
386 }
387
test_early_fail_and_wait(void)388 static int test_early_fail_and_wait(void)
389 {
390 struct io_uring ring;
391 struct io_uring_sqe *sqe;
392 int ret, invalid_fd = 42;
393 struct iovec iov = { .iov_base = NULL, .iov_len = 0 };
394
395 /* create a new ring as it leaves it dirty */
396 ret = io_uring_queue_init(8, &ring, 0);
397 if (ret) {
398 printf("ring setup failed\n");
399 return 1;
400 }
401
402 sqe = io_uring_get_sqe(&ring);
403 if (!sqe) {
404 printf("get sqe failed\n");
405 goto err;
406 }
407
408 io_uring_prep_readv(sqe, invalid_fd, &iov, 1, 0);
409 sqe->flags |= IOSQE_IO_LINK;
410
411 sqe = io_uring_get_sqe(&ring);
412 if (!sqe) {
413 printf("get sqe failed\n");
414 goto err;
415 }
416
417 io_uring_prep_nop(sqe);
418
419 ret = io_uring_submit_and_wait(&ring, 2);
420 if (ret <= 0 && ret != -EAGAIN) {
421 printf("sqe submit failed: %d\n", ret);
422 goto err;
423 }
424
425 io_uring_queue_exit(&ring);
426 return 0;
427 err:
428 io_uring_queue_exit(&ring);
429 return 1;
430 }
431
main(int argc,char * argv[])432 int main(int argc, char *argv[])
433 {
434 struct io_uring ring, poll_ring;
435 int ret;
436
437 if (argc > 1)
438 return 0;
439
440 ret = io_uring_queue_init(8, &ring, 0);
441 if (ret) {
442 printf("ring setup failed\n");
443 return 1;
444
445 }
446
447 ret = io_uring_queue_init(8, &poll_ring, IORING_SETUP_IOPOLL);
448 if (ret) {
449 printf("poll_ring setup failed\n");
450 return 1;
451 }
452
453 ret = test_single_link(&ring);
454 if (ret) {
455 printf("test_single_link failed\n");
456 return ret;
457 }
458
459 ret = test_double_link(&ring);
460 if (ret) {
461 printf("test_double_link failed\n");
462 return ret;
463 }
464
465 ret = test_double_chain(&ring);
466 if (ret) {
467 printf("test_double_chain failed\n");
468 return ret;
469 }
470
471 ret = test_single_link_fail(&poll_ring);
472 if (ret) {
473 printf("test_single_link_fail failed\n");
474 return ret;
475 }
476
477 ret = test_single_hardlink(&ring);
478 if (ret) {
479 fprintf(stderr, "test_single_hardlink\n");
480 return ret;
481 }
482
483 ret = test_double_hardlink(&ring);
484 if (ret) {
485 fprintf(stderr, "test_double_hardlink\n");
486 return ret;
487 }
488
489 ret = test_early_fail_and_wait();
490 if (ret) {
491 fprintf(stderr, "test_early_fail_and_wait\n");
492 return ret;
493 }
494
495 return 0;
496 }
497