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