1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: run various shared buffer ring sanity checks
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 #include <sys/mman.h>
13
14 #include "liburing.h"
15 #include "helpers.h"
16
17 static int no_buf_ring;
18 static int pagesize;
19
20 /* test trying to register classic group when ring group exists */
test_mixed_reg2(int bgid)21 static int test_mixed_reg2(int bgid)
22 {
23 struct io_uring_buf_ring *br;
24 struct io_uring_sqe *sqe;
25 struct io_uring_cqe *cqe;
26 struct io_uring ring;
27 void *bufs;
28 int ret;
29
30 ret = t_create_ring(1, &ring, 0);
31 if (ret == T_SETUP_SKIP)
32 return 0;
33 else if (ret != T_SETUP_OK)
34 return 1;
35
36 br = io_uring_setup_buf_ring(&ring, 32, bgid, 0, &ret);
37 if (!br) {
38 fprintf(stderr, "Buffer ring register failed %d\n", ret);
39 return 1;
40 }
41
42 /* provide classic buffers, group 1 */
43 bufs = malloc(8 * 1024);
44 sqe = io_uring_get_sqe(&ring);
45 io_uring_prep_provide_buffers(sqe, bufs, 1024, 8, bgid, 0);
46 io_uring_submit(&ring);
47 ret = io_uring_wait_cqe(&ring, &cqe);
48 if (ret) {
49 fprintf(stderr, "wait_cqe %d\n", ret);
50 return 1;
51 }
52 if (cqe->res != -EEXIST && cqe->res != -EINVAL) {
53 fprintf(stderr, "cqe res %d\n", cqe->res);
54 return 1;
55 }
56 io_uring_cqe_seen(&ring, cqe);
57
58 io_uring_free_buf_ring(&ring, br, 32, bgid);
59 io_uring_queue_exit(&ring);
60 return 0;
61 }
62
63 /* test trying to register ring group when classic group exists */
test_mixed_reg(int bgid)64 static int test_mixed_reg(int bgid)
65 {
66 struct io_uring_buf_ring *br;
67 struct io_uring_sqe *sqe;
68 struct io_uring_cqe *cqe;
69 struct io_uring ring;
70 void *bufs;
71 int ret;
72
73 ret = t_create_ring(1, &ring, 0);
74 if (ret == T_SETUP_SKIP)
75 return 0;
76 else if (ret != T_SETUP_OK)
77 return 1;
78
79 /* provide classic buffers, group 1 */
80 bufs = malloc(8 * 1024);
81 sqe = io_uring_get_sqe(&ring);
82 io_uring_prep_provide_buffers(sqe, bufs, 1024, 8, bgid, 0);
83 io_uring_submit(&ring);
84 ret = io_uring_wait_cqe(&ring, &cqe);
85 if (ret) {
86 fprintf(stderr, "wait_cqe %d\n", ret);
87 return 1;
88 }
89 if (cqe->res) {
90 fprintf(stderr, "cqe res %d\n", cqe->res);
91 return 1;
92 }
93 io_uring_cqe_seen(&ring, cqe);
94
95 br = io_uring_setup_buf_ring(&ring, 32, bgid, 0, &ret);
96 if (br) {
97 fprintf(stderr, "Buffer ring setup succeeded unexpectedly %d\n", ret);
98 return 1;
99 }
100
101 io_uring_queue_exit(&ring);
102 return 0;
103 }
104
test_double_reg_unreg(int bgid)105 static int test_double_reg_unreg(int bgid)
106 {
107 struct io_uring_buf_reg reg = { };
108 struct io_uring_buf_ring *br;
109 struct io_uring ring;
110 int ret;
111
112 ret = t_create_ring(1, &ring, 0);
113 if (ret == T_SETUP_SKIP)
114 return 0;
115 else if (ret != T_SETUP_OK)
116 return 1;
117
118 br = io_uring_setup_buf_ring(&ring, 32, bgid, 0, &ret);
119 if (!br) {
120 fprintf(stderr, "Buffer ring register failed %d\n", ret);
121 return 1;
122 }
123
124 /* check that 2nd register with same bgid fails */
125 reg.ring_addr = (unsigned long) br;
126 reg.ring_entries = 32;
127 reg.bgid = bgid;
128
129 ret = io_uring_register_buf_ring(&ring, ®, 0);
130 if (ret != -EEXIST) {
131 fprintf(stderr, "Buffer ring register failed %d\n", ret);
132 return 1;
133 }
134
135 ret = io_uring_free_buf_ring(&ring, br, 32, bgid);
136 if (ret) {
137 fprintf(stderr, "Buffer ring register failed %d\n", ret);
138 return 1;
139 }
140
141 ret = io_uring_unregister_buf_ring(&ring, bgid);
142 if (ret != -EINVAL && ret != -ENOENT) {
143 fprintf(stderr, "Buffer ring register failed %d\n", ret);
144 return 1;
145 }
146
147 io_uring_queue_exit(&ring);
148 return 0;
149 }
150
test_reg_unreg(int bgid)151 static int test_reg_unreg(int bgid)
152 {
153 struct io_uring_buf_ring *br;
154 struct io_uring ring;
155 int ret;
156
157 ret = t_create_ring(1, &ring, 0);
158 if (ret == T_SETUP_SKIP)
159 return 0;
160 else if (ret != T_SETUP_OK)
161 return 1;
162
163 br = io_uring_setup_buf_ring(&ring, 32, bgid, 0, &ret);
164 if (!br) {
165 if (ret == -EINVAL) {
166 no_buf_ring = 1;
167 return 0;
168 }
169 fprintf(stderr, "Buffer ring register failed %d\n", ret);
170 return 1;
171 }
172
173 ret = io_uring_free_buf_ring(&ring, br, 32, bgid);
174 if (ret) {
175 fprintf(stderr, "Buffer ring unregister failed %d\n", ret);
176 return 1;
177 }
178
179 io_uring_queue_exit(&ring);
180 return 0;
181 }
182
test_bad_reg(int bgid)183 static int test_bad_reg(int bgid)
184 {
185 struct io_uring ring;
186 int ret;
187 struct io_uring_buf_reg reg = { };
188
189 ret = t_create_ring(1, &ring, 0);
190 if (ret == T_SETUP_SKIP)
191 return 0;
192 else if (ret != T_SETUP_OK)
193 return 1;
194
195 reg.ring_addr = 4096;
196 reg.ring_entries = 32;
197 reg.bgid = bgid;
198
199 ret = io_uring_register_buf_ring(&ring, ®, 0);
200 if (!ret)
201 fprintf(stderr, "Buffer ring register worked unexpectedly\n");
202
203 io_uring_queue_exit(&ring);
204 return !ret;
205 }
206
test_full_page_reg(int bgid)207 static int test_full_page_reg(int bgid)
208 {
209 #if defined(__hppa__)
210 return T_EXIT_SKIP;
211 #else
212 struct io_uring ring;
213 int ret;
214 void *ptr;
215 struct io_uring_buf_reg reg = { };
216 int entries = pagesize / sizeof(struct io_uring_buf);
217
218 ret = io_uring_queue_init(1, &ring, 0);
219 if (ret) {
220 fprintf(stderr, "queue init failed %d\n", ret);
221 return T_EXIT_FAIL;
222 }
223
224 ret = posix_memalign(&ptr, pagesize, pagesize * 2);
225 if (ret) {
226 fprintf(stderr, "posix_memalign failed %d\n", ret);
227 goto err;
228 }
229
230 ret = mprotect(ptr + pagesize, pagesize, PROT_NONE);
231 if (ret) {
232 fprintf(stderr, "mprotect failed %d\n", errno);
233 goto err1;
234 }
235
236 reg.ring_addr = (unsigned long) ptr;
237 reg.ring_entries = entries;
238 reg.bgid = bgid;
239
240 ret = io_uring_register_buf_ring(&ring, ®, 0);
241 if (ret)
242 fprintf(stderr, "register buf ring failed %d\n", ret);
243
244 if (mprotect(ptr + pagesize, pagesize, PROT_READ | PROT_WRITE))
245 fprintf(stderr, "reverting mprotect failed %d\n", errno);
246
247 err1:
248 free(ptr);
249 err:
250 io_uring_queue_exit(&ring);
251 return ret ? T_EXIT_FAIL : T_EXIT_PASS;
252 #endif
253 }
254
test_one_read(int fd,int bgid,struct io_uring * ring)255 static int test_one_read(int fd, int bgid, struct io_uring *ring)
256 {
257 int ret;
258 struct io_uring_cqe *cqe;
259 struct io_uring_sqe *sqe;
260
261 sqe = io_uring_get_sqe(ring);
262 if (!sqe) {
263 fprintf(stderr, "get sqe failed\n");
264 return -1;
265 }
266
267 io_uring_prep_read(sqe, fd, NULL, 1, 0);
268 sqe->flags |= IOSQE_BUFFER_SELECT;
269 sqe->buf_group = bgid;
270 ret = io_uring_submit(ring);
271 if (ret <= 0) {
272 fprintf(stderr, "sqe submit failed: %d\n", ret);
273 return -1;
274 }
275
276 ret = io_uring_wait_cqe(ring, &cqe);
277 if (ret < 0) {
278 fprintf(stderr, "wait completion %d\n", ret);
279 return -1;
280 }
281 ret = cqe->res;
282 io_uring_cqe_seen(ring, cqe);
283
284 if (ret == -ENOBUFS)
285 return ret;
286
287 if (ret != 1) {
288 fprintf(stderr, "read result %d\n", ret);
289 return -1;
290 }
291
292 return cqe->flags >> 16;
293 }
294
test_running(int bgid,int entries,int loops,int use_mmap)295 static int test_running(int bgid, int entries, int loops, int use_mmap)
296 {
297 int ring_mask = io_uring_buf_ring_mask(entries);
298 struct io_uring_buf_ring *br;
299 int ret, loop, idx, read_fd;
300 struct io_uring ring;
301 char buffer[8];
302 bool *buffers;
303
304 ret = t_create_ring(1, &ring, 0);
305 if (ret == T_SETUP_SKIP)
306 return T_EXIT_SKIP;
307 else if (ret != T_SETUP_OK)
308 return T_EXIT_FAIL;
309
310 if (!use_mmap) {
311 br = io_uring_setup_buf_ring(&ring, entries, bgid, 0, &ret);
312 if (!br) {
313 /* by now should have checked if this is supported or not */
314 fprintf(stderr, "Buffer ring register failed %d\n", ret);
315 return T_EXIT_FAIL;
316 }
317 } else {
318 struct io_uring_buf_reg reg = {
319 .ring_entries = entries,
320 .bgid = bgid,
321 .flags = IOU_PBUF_RING_MMAP,
322 };
323 size_t ring_size;
324 off_t off;
325
326 ret = io_uring_register_buf_ring(&ring, ®, 0);
327 if (ret) {
328 if (ret == -EINVAL)
329 return T_EXIT_SKIP;
330 fprintf(stderr, "mmap ring register failed %d\n", ret);
331 return T_EXIT_FAIL;
332 }
333
334 off = IORING_OFF_PBUF_RING |
335 (unsigned long long) bgid << IORING_OFF_PBUF_SHIFT;
336 ring_size = sizeof(struct io_uring_buf) * entries;
337 br = mmap(NULL, ring_size, PROT_READ | PROT_WRITE,
338 MAP_SHARED | MAP_POPULATE, ring.ring_fd, off);
339 if (br == MAP_FAILED) {
340 perror("mmap");
341 return T_EXIT_FAIL;
342 }
343 }
344
345 buffers = malloc(sizeof(bool) * entries);
346 if (!buffers)
347 return T_EXIT_SKIP;
348
349 read_fd = open("/dev/zero", O_RDONLY);
350 if (read_fd < 0)
351 return T_EXIT_SKIP;
352
353 for (loop = 0; loop < loops; loop++) {
354 memset(buffers, 0, sizeof(bool) * entries);
355 for (idx = 0; idx < entries; idx++)
356 io_uring_buf_ring_add(br, buffer, sizeof(buffer), idx, ring_mask, idx);
357 io_uring_buf_ring_advance(br, entries);
358
359 for (idx = 0; idx < entries; idx++) {
360 memset(buffer, 1, sizeof(buffer));
361 ret = test_one_read(read_fd, bgid, &ring);
362 if (ret < 0) {
363 fprintf(stderr, "bad run %d/%d = %d\n", loop, idx, ret);
364 return T_EXIT_FAIL;
365 }
366 if (buffers[ret]) {
367 fprintf(stderr, "reused buffer %d/%d = %d!\n", loop, idx, ret);
368 return T_EXIT_FAIL;
369 }
370 if (buffer[0] != 0) {
371 fprintf(stderr, "unexpected read %d %d/%d = %d!\n",
372 (int)buffer[0], loop, idx, ret);
373 return T_EXIT_FAIL;
374 }
375 if (buffer[1] != 1) {
376 fprintf(stderr, "unexpected spilled read %d %d/%d = %d!\n",
377 (int)buffer[1], loop, idx, ret);
378 return T_EXIT_FAIL;
379 }
380 buffers[ret] = true;
381 }
382 ret = test_one_read(read_fd, bgid, &ring);
383 if (ret != -ENOBUFS) {
384 fprintf(stderr, "expected enobufs run %d = %d\n", loop, ret);
385 return T_EXIT_FAIL;
386 }
387
388 }
389
390 ret = io_uring_unregister_buf_ring(&ring, bgid);
391 if (ret) {
392 fprintf(stderr, "Buffer ring register failed %d\n", ret);
393 return T_EXIT_FAIL;
394 }
395
396 close(read_fd);
397 io_uring_queue_exit(&ring);
398 free(buffers);
399 return T_EXIT_PASS;
400 }
401
main(int argc,char * argv[])402 int main(int argc, char *argv[])
403 {
404 int bgids[] = { 1, 127, -1 };
405 int entries[] = {1, 32768, 4096, -1 };
406 int ret, i;
407
408 if (argc > 1)
409 return T_EXIT_SKIP;
410
411 pagesize = getpagesize();
412
413 for (i = 0; bgids[i] != -1; i++) {
414 ret = test_reg_unreg(bgids[i]);
415 if (ret) {
416 fprintf(stderr, "test_reg_unreg failed\n");
417 return T_EXIT_FAIL;
418 }
419 if (no_buf_ring)
420 break;
421
422 ret = test_bad_reg(bgids[i]);
423 if (ret) {
424 fprintf(stderr, "test_bad_reg failed\n");
425 return T_EXIT_FAIL;
426 }
427
428 ret = test_double_reg_unreg(bgids[i]);
429 if (ret) {
430 fprintf(stderr, "test_double_reg_unreg failed\n");
431 return T_EXIT_FAIL;
432 }
433
434 ret = test_mixed_reg(bgids[i]);
435 if (ret) {
436 fprintf(stderr, "test_mixed_reg failed\n");
437 return T_EXIT_FAIL;
438 }
439
440 ret = test_mixed_reg2(bgids[i]);
441 if (ret) {
442 fprintf(stderr, "test_mixed_reg2 failed\n");
443 return T_EXIT_FAIL;
444 }
445
446 ret = test_full_page_reg(bgids[i]);
447 if (ret == T_EXIT_FAIL) {
448 fprintf(stderr, "test_full_page_reg failed\n");
449 return T_EXIT_FAIL;
450 }
451 }
452
453 for (i = 0; !no_buf_ring && entries[i] != -1; i++) {
454 ret = test_running(2, entries[i], 3, 0);
455 if (ret) {
456 fprintf(stderr, "test_running(%d) failed\n", entries[i]);
457 return T_EXIT_FAIL;
458 }
459 }
460
461 for (i = 0; !no_buf_ring && entries[i] != -1; i++) {
462 ret = test_running(2, entries[i], 3, 1);
463 if (ret == T_EXIT_SKIP) {
464 break;
465 } else if (ret != T_EXIT_PASS) {
466 fprintf(stderr, "test_running(%d) mmap failed\n", entries[i]);
467 return T_EXIT_FAIL;
468 }
469 }
470
471
472 return T_EXIT_PASS;
473 }
474