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