• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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