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