1 /* SPDX-License-Identifier: MIT */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <assert.h>
9
10 #include "liburing.h"
11 #include "helpers.h"
12
13 #define LINK_SIZE 6
14 #define TIMEOUT_USER_DATA (-1)
15
16 static int fds[2];
17
18 /* should be successfully submitted but fails during execution */
prep_exec_fail_req(struct io_uring_sqe * sqe)19 static void prep_exec_fail_req(struct io_uring_sqe *sqe)
20 {
21 io_uring_prep_write(sqe, fds[1], NULL, 100, 0);
22 }
23
test_link_success(struct io_uring * ring,int nr,bool skip_last)24 static int test_link_success(struct io_uring *ring, int nr, bool skip_last)
25 {
26 struct io_uring_cqe *cqe;
27 struct io_uring_sqe *sqe;
28 int ret, i;
29
30 for (i = 0; i < nr; ++i) {
31 sqe = io_uring_get_sqe(ring);
32 io_uring_prep_nop(sqe);
33 if (i != nr - 1 || skip_last)
34 sqe->flags |= IOSQE_IO_LINK | IOSQE_CQE_SKIP_SUCCESS;
35 sqe->user_data = i;
36 }
37
38 ret = io_uring_submit(ring);
39 if (ret != nr) {
40 fprintf(stderr, "sqe submit failed: %d\n", ret);
41 goto err;
42 }
43
44 if (!skip_last) {
45 ret = io_uring_wait_cqe(ring, &cqe);
46 if (ret != 0) {
47 fprintf(stderr, "wait completion %d\n", ret);
48 goto err;
49 }
50 if (cqe->res != 0) {
51 fprintf(stderr, "nop failed: res %d\n", cqe->res);
52 goto err;
53 }
54 if (cqe->user_data != nr - 1) {
55 fprintf(stderr, "invalid user_data %i\n", (int)cqe->user_data);
56 goto err;
57 }
58 io_uring_cqe_seen(ring, cqe);
59 }
60
61 if (io_uring_peek_cqe(ring, &cqe) >= 0) {
62 fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
63 goto err;
64 }
65 return 0;
66 err:
67 return 1;
68 }
69
test_link_fail(struct io_uring * ring,int nr,int fail_idx)70 static int test_link_fail(struct io_uring *ring, int nr, int fail_idx)
71 {
72 struct io_uring_cqe *cqe;
73 struct io_uring_sqe *sqe;
74 int ret, i;
75
76 for (i = 0; i < nr; ++i) {
77 sqe = io_uring_get_sqe(ring);
78 if (i == fail_idx)
79 prep_exec_fail_req(sqe);
80 else
81 io_uring_prep_nop(sqe);
82
83 if (i != nr - 1)
84 sqe->flags |= IOSQE_IO_LINK | IOSQE_CQE_SKIP_SUCCESS;
85 sqe->user_data = i;
86 }
87
88 ret = io_uring_submit(ring);
89 if (ret != nr) {
90 fprintf(stderr, "sqe submit failed: %d\n", ret);
91 goto err;
92 }
93 ret = io_uring_wait_cqe(ring, &cqe);
94 if (ret != 0) {
95 fprintf(stderr, "wait completion %d\n", ret);
96 goto err;
97 }
98 if (!cqe->res || cqe->user_data != fail_idx) {
99 fprintf(stderr, "got: user_data %d res %d, expected data: %d\n",
100 (int)cqe->user_data, cqe->res, fail_idx);
101 goto err;
102 }
103 io_uring_cqe_seen(ring, cqe);
104
105 if (io_uring_peek_cqe(ring, &cqe) >= 0) {
106 fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
107 goto err;
108 }
109 return 0;
110 err:
111 return 1;
112 }
113
test_ltimeout_cancel(struct io_uring * ring,int nr,int tout_idx,bool async,int fail_idx)114 static int test_ltimeout_cancel(struct io_uring *ring, int nr, int tout_idx,
115 bool async, int fail_idx)
116 {
117 struct __kernel_timespec ts = {.tv_sec = 1, .tv_nsec = 0};
118 struct io_uring_cqe *cqe;
119 struct io_uring_sqe *sqe;
120 int ret, i;
121 int e_res = 0, e_idx = nr - 1;
122
123 if (fail_idx >= 0) {
124 e_res = -EFAULT;
125 e_idx = fail_idx;
126 }
127
128 for (i = 0; i < nr; ++i) {
129 sqe = io_uring_get_sqe(ring);
130 if (i == fail_idx)
131 prep_exec_fail_req(sqe);
132 else
133 io_uring_prep_nop(sqe);
134 sqe->user_data = i;
135 sqe->flags |= IOSQE_IO_LINK;
136 if (async)
137 sqe->flags |= IOSQE_ASYNC;
138 if (i != nr - 1)
139 sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
140
141 if (i == tout_idx) {
142 sqe = io_uring_get_sqe(ring);
143 io_uring_prep_link_timeout(sqe, &ts, 0);
144 sqe->flags |= IOSQE_IO_LINK | IOSQE_CQE_SKIP_SUCCESS;
145 sqe->user_data = TIMEOUT_USER_DATA;
146 }
147 }
148
149 ret = io_uring_submit(ring);
150 if (ret != nr + 1) {
151 fprintf(stderr, "sqe submit failed: %d\n", ret);
152 goto err;
153 }
154 ret = io_uring_wait_cqe(ring, &cqe);
155 if (ret != 0) {
156 fprintf(stderr, "wait completion %d\n", ret);
157 goto err;
158 }
159 if (cqe->user_data != e_idx) {
160 fprintf(stderr, "invalid user_data %i\n", (int)cqe->user_data);
161 goto err;
162 }
163 if (cqe->res != e_res) {
164 fprintf(stderr, "unexpected res: %d\n", cqe->res);
165 goto err;
166 }
167 io_uring_cqe_seen(ring, cqe);
168
169 if (io_uring_peek_cqe(ring, &cqe) >= 0) {
170 fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
171 goto err;
172 }
173 return 0;
174 err:
175 return 1;
176 }
177
test_ltimeout_fire(struct io_uring * ring,bool async,bool skip_main,bool skip_tout)178 static int test_ltimeout_fire(struct io_uring *ring, bool async,
179 bool skip_main, bool skip_tout)
180 {
181 char buf[1];
182 struct __kernel_timespec ts = {.tv_sec = 0, .tv_nsec = 1000000};
183 struct io_uring_cqe *cqe;
184 struct io_uring_sqe *sqe;
185 int ret, i;
186 int nr = 1 + !skip_tout;
187
188 sqe = io_uring_get_sqe(ring);
189 io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0);
190 sqe->flags |= IOSQE_IO_LINK;
191 sqe->flags |= async ? IOSQE_ASYNC : 0;
192 sqe->flags |= skip_main ? IOSQE_CQE_SKIP_SUCCESS : 0;
193 sqe->user_data = 0;
194
195 sqe = io_uring_get_sqe(ring);
196 io_uring_prep_link_timeout(sqe, &ts, 0);
197 sqe->flags |= skip_tout ? IOSQE_CQE_SKIP_SUCCESS : 0;
198 sqe->user_data = 1;
199
200 ret = io_uring_submit(ring);
201 if (ret != 2) {
202 fprintf(stderr, "sqe submit failed: %d\n", ret);
203 return 1;
204 }
205
206 for (i = 0; i < nr; i++) {
207 ret = io_uring_wait_cqe(ring, &cqe);
208 if (ret != 0) {
209 fprintf(stderr, "wait completion %d\n", ret);
210 return 1;
211 }
212 switch (cqe->user_data) {
213 case 0:
214 if (cqe->res != -ECANCELED && cqe->res != -EINTR) {
215 fprintf(stderr, "unexpected read return: %d\n", cqe->res);
216 return 1;
217 }
218 break;
219 case 1:
220 if (skip_tout) {
221 fprintf(stderr, "extra timeout cqe, %d\n", cqe->res);
222 return 1;
223 }
224 break;
225 }
226 io_uring_cqe_seen(ring, cqe);
227 }
228
229
230 if (io_uring_peek_cqe(ring, &cqe) >= 0) {
231 fprintf(stderr, "single CQE expected: got data: %i res: %i\n",
232 (int)cqe->user_data, cqe->res);
233 return 1;
234 }
235 return 0;
236 }
237
test_hardlink(struct io_uring * ring,int nr,int fail_idx,int skip_idx,bool hardlink_last)238 static int test_hardlink(struct io_uring *ring, int nr, int fail_idx,
239 int skip_idx, bool hardlink_last)
240 {
241 struct io_uring_cqe *cqe;
242 struct io_uring_sqe *sqe;
243 int ret, i;
244
245 assert(fail_idx < nr);
246 assert(skip_idx < nr);
247
248 for (i = 0; i < nr; i++) {
249 sqe = io_uring_get_sqe(ring);
250 if (i == fail_idx)
251 prep_exec_fail_req(sqe);
252 else
253 io_uring_prep_nop(sqe);
254 if (i != nr - 1 || hardlink_last)
255 sqe->flags |= IOSQE_IO_HARDLINK;
256 if (i == skip_idx)
257 sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
258 sqe->user_data = i;
259 }
260
261 ret = io_uring_submit(ring);
262 if (ret != nr) {
263 fprintf(stderr, "sqe submit failed: %d\n", ret);
264 goto err;
265 }
266
267 for (i = 0; i < nr; i++) {
268 if (i == skip_idx && fail_idx != skip_idx)
269 continue;
270
271 ret = io_uring_wait_cqe(ring, &cqe);
272 if (ret != 0) {
273 fprintf(stderr, "wait completion %d\n", ret);
274 goto err;
275 }
276 if (cqe->user_data != i) {
277 fprintf(stderr, "invalid user_data %d (%i)\n",
278 (int)cqe->user_data, i);
279 goto err;
280 }
281 if (i == fail_idx) {
282 if (cqe->res >= 0) {
283 fprintf(stderr, "req should've failed %d %d\n",
284 (int)cqe->user_data, cqe->res);
285 goto err;
286 }
287 } else {
288 if (cqe->res) {
289 fprintf(stderr, "req error %d %d\n",
290 (int)cqe->user_data, cqe->res);
291 goto err;
292 }
293 }
294
295 io_uring_cqe_seen(ring, cqe);
296 }
297
298 if (io_uring_peek_cqe(ring, &cqe) >= 0) {
299 fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
300 goto err;
301 }
302 return 0;
303 err:
304 return 1;
305 }
306
main(int argc,char * argv[])307 int main(int argc, char *argv[])
308 {
309 struct io_uring ring;
310 int ret, i, j, k;
311 int mid_idx = LINK_SIZE / 2;
312 int last_idx = LINK_SIZE - 1;
313
314 if (argc > 1)
315 return 0;
316
317 if (pipe(fds)) {
318 fprintf(stderr, "pipe() failed\n");
319 return 1;
320 }
321 ret = io_uring_queue_init(16, &ring, 0);
322 if (ret) {
323 fprintf(stderr, "ring setup failed: %d\n", ret);
324 return 1;
325 }
326
327 if (!(ring.features & IORING_FEAT_CQE_SKIP))
328 return T_EXIT_SKIP;
329
330 for (i = 0; i < 4; i++) {
331 bool skip_last = i & 1;
332 int sz = (i & 2) ? LINK_SIZE : 1;
333
334 ret = test_link_success(&ring, sz, skip_last);
335 if (ret) {
336 fprintf(stderr, "test_link_success sz %d, %d last\n",
337 skip_last, sz);
338 return ret;
339 }
340 }
341
342 ret = test_link_fail(&ring, LINK_SIZE, mid_idx);
343 if (ret) {
344 fprintf(stderr, "test_link_fail mid failed\n");
345 return ret;
346 }
347
348 ret = test_link_fail(&ring, LINK_SIZE, last_idx);
349 if (ret) {
350 fprintf(stderr, "test_link_fail last failed\n");
351 return ret;
352 }
353
354 for (i = 0; i < 2; i++) {
355 bool async = i & 1;
356
357 ret = test_ltimeout_cancel(&ring, 1, 0, async, -1);
358 if (ret) {
359 fprintf(stderr, "test_ltimeout_cancel 1 failed, %i\n",
360 async);
361 return ret;
362 }
363 ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, -1);
364 if (ret) {
365 fprintf(stderr, "test_ltimeout_cancel mid failed, %i\n",
366 async);
367 return ret;
368 }
369 ret = test_ltimeout_cancel(&ring, LINK_SIZE, last_idx, async, -1);
370 if (ret) {
371 fprintf(stderr, "test_ltimeout_cancel last failed, %i\n",
372 async);
373 return ret;
374 }
375 ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, mid_idx);
376 if (ret) {
377 fprintf(stderr, "test_ltimeout_cancel fail mid failed, %i\n",
378 async);
379 return ret;
380 }
381 ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, mid_idx - 1);
382 if (ret) {
383 fprintf(stderr, "test_ltimeout_cancel fail2 mid failed, %i\n",
384 async);
385 return ret;
386 }
387 ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, mid_idx + 1);
388 if (ret) {
389 fprintf(stderr, "test_ltimeout_cancel fail3 mid failed, %i\n",
390 async);
391 return ret;
392 }
393 }
394
395 for (i = 0; i < 8; i++) {
396 bool async = i & 1;
397 bool skip1 = i & 2;
398 bool skip2 = i & 4;
399
400 ret = test_ltimeout_fire(&ring, async, skip1, skip2);
401 if (ret) {
402 fprintf(stderr, "test_ltimeout_fire failed\n");
403 return ret;
404 }
405 }
406
407 /* test 3 positions, start/middle/end of the link, i.e. indexes 0, 3, 6 */
408 for (i = 0; i < 3; i++) {
409 for (j = 0; j < 3; j++) {
410 for (k = 0; k < 2; k++) {
411 bool mark_last = k & 1;
412
413 ret = test_hardlink(&ring, 7, i * 3, j * 3, mark_last);
414 if (ret) {
415 fprintf(stderr, "test_hardlink failed"
416 "fail %i skip %i mark last %i\n",
417 i * 3, j * 3, k);
418 return 1;
419 }
420 }
421 }
422 }
423
424 close(fds[0]);
425 close(fds[1]);
426 io_uring_queue_exit(&ring);
427 return 0;
428 }
429