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