xref: /aosp_15_r20/external/liburing/test/cq-overflow.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: run various CQ ring overflow 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 "helpers.h"
14 #include "liburing.h"
15 
16 #define FILE_SIZE	(256 * 1024)
17 #define BS		4096
18 #define BUFFERS		(FILE_SIZE / BS)
19 
20 static struct iovec *vecs;
21 
22 #define ENTRIES	8
23 
test_io(const char * file,unsigned long usecs,unsigned * drops,int fault)24 static int test_io(const char *file, unsigned long usecs, unsigned *drops, int fault)
25 {
26 	struct io_uring_sqe *sqe;
27 	struct io_uring_cqe *cqe;
28 	struct io_uring_params p;
29 	unsigned reaped, total;
30 	struct io_uring ring;
31 	int nodrop, i, fd, ret;
32 
33 	fd = open(file, O_RDONLY | O_DIRECT);
34 	if (fd < 0) {
35 		perror("file open");
36 		goto err;
37 	}
38 
39 	memset(&p, 0, sizeof(p));
40 	ret = io_uring_queue_init_params(ENTRIES, &ring, &p);
41 	if (ret) {
42 		fprintf(stderr, "ring create failed: %d\n", ret);
43 		goto err;
44 	}
45 	nodrop = 0;
46 	if (p.features & IORING_FEAT_NODROP)
47 		nodrop = 1;
48 
49 	total = 0;
50 	for (i = 0; i < BUFFERS / 2; i++) {
51 		off_t offset;
52 
53 		sqe = io_uring_get_sqe(&ring);
54 		if (!sqe) {
55 			fprintf(stderr, "sqe get failed\n");
56 			goto err;
57 		}
58 		offset = BS * (rand() % BUFFERS);
59 		if (fault && i == ENTRIES + 4)
60 			vecs[i].iov_base = NULL;
61 		io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
62 
63 		ret = io_uring_submit(&ring);
64 		if (nodrop && ret == -EBUSY) {
65 			*drops = 1;
66 			total = i;
67 			break;
68 		} else if (ret != 1) {
69 			fprintf(stderr, "submit got %d, wanted %d\n", ret, 1);
70 			total = i;
71 			break;
72 		}
73 		total++;
74 	}
75 
76 	if (*drops)
77 		goto reap_it;
78 
79 	usleep(usecs);
80 
81 	for (i = total; i < BUFFERS; i++) {
82 		off_t offset;
83 
84 		sqe = io_uring_get_sqe(&ring);
85 		if (!sqe) {
86 			fprintf(stderr, "sqe get failed\n");
87 			goto err;
88 		}
89 		offset = BS * (rand() % BUFFERS);
90 		io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
91 
92 		ret = io_uring_submit(&ring);
93 		if (nodrop && ret == -EBUSY) {
94 			*drops = 1;
95 			break;
96 		} else if (ret != 1) {
97 			fprintf(stderr, "submit got %d, wanted %d\n", ret, 1);
98 			break;
99 		}
100 		total++;
101 	}
102 
103 reap_it:
104 	reaped = 0;
105 	do {
106 		if (nodrop) {
107 			/* nodrop should never lose events */
108 			if (reaped == total)
109 				break;
110 		} else {
111 			if (reaped + *ring.cq.koverflow == total)
112 				break;
113 		}
114 		ret = io_uring_wait_cqe(&ring, &cqe);
115 		if (ret) {
116 			fprintf(stderr, "wait_cqe=%d\n", ret);
117 			goto err;
118 		}
119 		if (cqe->res != BS) {
120 			if (!(fault && cqe->res == -EFAULT)) {
121 				fprintf(stderr, "cqe res %d, wanted %d\n",
122 						cqe->res, BS);
123 				goto err;
124 			}
125 		}
126 		io_uring_cqe_seen(&ring, cqe);
127 		reaped++;
128 	} while (1);
129 
130 	if (!io_uring_peek_cqe(&ring, &cqe)) {
131 		fprintf(stderr, "found unexpected completion\n");
132 		goto err;
133 	}
134 
135 	if (!nodrop) {
136 		*drops = *ring.cq.koverflow;
137 	} else if (*ring.cq.koverflow) {
138 		fprintf(stderr, "Found %u overflows\n", *ring.cq.koverflow);
139 		goto err;
140 	}
141 
142 	io_uring_queue_exit(&ring);
143 	close(fd);
144 	return 0;
145 err:
146 	if (fd != -1)
147 		close(fd);
148 	io_uring_queue_exit(&ring);
149 	return 1;
150 }
151 
reap_events(struct io_uring * ring,unsigned nr_events,int do_wait)152 static int reap_events(struct io_uring *ring, unsigned nr_events, int do_wait)
153 {
154 	struct io_uring_cqe *cqe;
155 	int i, ret = 0, seq = 0;
156 
157 	for (i = 0; i < nr_events; i++) {
158 		if (do_wait)
159 			ret = io_uring_wait_cqe(ring, &cqe);
160 		else
161 			ret = io_uring_peek_cqe(ring, &cqe);
162 		if (ret) {
163 			if (ret != -EAGAIN)
164 				fprintf(stderr, "cqe peek failed: %d\n", ret);
165 			break;
166 		}
167 		if (cqe->user_data != seq) {
168 			fprintf(stderr, "cqe sequence out-of-order\n");
169 			fprintf(stderr, "got %d, wanted %d\n", (int) cqe->user_data,
170 					seq);
171 			return -EINVAL;
172 		}
173 		seq++;
174 		io_uring_cqe_seen(ring, cqe);
175 	}
176 
177 	return i ? i : ret;
178 }
179 
180 /*
181  * Submit some NOPs and watch if the overflow is correct
182  */
test_overflow(void)183 static int test_overflow(void)
184 {
185 	struct io_uring ring;
186 	struct io_uring_params p;
187 	struct io_uring_sqe *sqe;
188 	unsigned pending;
189 	int ret, i, j;
190 
191 	memset(&p, 0, sizeof(p));
192 	ret = io_uring_queue_init_params(4, &ring, &p);
193 	if (ret) {
194 		fprintf(stderr, "io_uring_queue_init failed %d\n", ret);
195 		return 1;
196 	}
197 
198 	/* submit 4x4 SQEs, should overflow the ring by 8 */
199 	pending = 0;
200 	for (i = 0; i < 4; i++) {
201 		for (j = 0; j < 4; j++) {
202 			sqe = io_uring_get_sqe(&ring);
203 			if (!sqe) {
204 				fprintf(stderr, "get sqe failed\n");
205 				goto err;
206 			}
207 
208 			io_uring_prep_nop(sqe);
209 			sqe->user_data = (i * 4) + j;
210 		}
211 
212 		ret = io_uring_submit(&ring);
213 		if (ret == 4) {
214 			pending += 4;
215 			continue;
216 		}
217 		if (p.features & IORING_FEAT_NODROP) {
218 			if (ret == -EBUSY)
219 				break;
220 		}
221 		fprintf(stderr, "sqe submit failed: %d\n", ret);
222 		goto err;
223 	}
224 
225 	/* we should now have 8 completions ready */
226 	ret = reap_events(&ring, pending, 0);
227 	if (ret < 0)
228 		goto err;
229 
230 	if (!(p.features & IORING_FEAT_NODROP)) {
231 		if (*ring.cq.koverflow != 8) {
232 			fprintf(stderr, "cq ring overflow %d, expected 8\n",
233 					*ring.cq.koverflow);
234 			goto err;
235 		}
236 	}
237 	io_uring_queue_exit(&ring);
238 	return 0;
239 err:
240 	io_uring_queue_exit(&ring);
241 	return 1;
242 }
243 
main(int argc,char * argv[])244 int main(int argc, char *argv[])
245 {
246 	const char *fname = ".cq-overflow";
247 	unsigned iters, drops;
248 	unsigned long usecs;
249 	int ret;
250 
251 	if (argc > 1)
252 		return 0;
253 
254 	ret = test_overflow();
255 	if (ret) {
256 		printf("test_overflow failed\n");
257 		return ret;
258 	}
259 
260 	t_create_file(fname, FILE_SIZE);
261 
262 	vecs = t_create_buffers(BUFFERS, BS);
263 
264 	iters = 0;
265 	usecs = 1000;
266 	do {
267 		drops = 0;
268 
269 		if (test_io(fname, usecs, &drops, 0)) {
270 			fprintf(stderr, "test_io nofault failed\n");
271 			goto err;
272 		}
273 		if (drops)
274 			break;
275 		usecs = (usecs * 12) / 10;
276 		iters++;
277 	} while (iters < 40);
278 
279 	if (test_io(fname, usecs, &drops, 0)) {
280 		fprintf(stderr, "test_io nofault failed\n");
281 		goto err;
282 	}
283 
284 	if (test_io(fname, usecs, &drops, 1)) {
285 		fprintf(stderr, "test_io fault failed\n");
286 		goto err;
287 	}
288 
289 	unlink(fname);
290 	return 0;
291 err:
292 	unlink(fname);
293 	return 1;
294 }
295