xref: /aosp_15_r20/external/liburing/test/skip-cqe.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
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