1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: ring mapped provided buffers with reads
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 "liburing.h"
14 #include "helpers.h"
15
16 #define BUF_SIZE 4096
17 #define NR_BUFS 64
18 #define FSIZE (BUF_SIZE * NR_BUFS)
19
20 #define BR_MASK (NR_BUFS - 1)
21
22 static int no_buf_ring;
23
verify_buffer(char * buf,char val)24 static int verify_buffer(char *buf, char val)
25 {
26 int i;
27
28 for (i = 0; i < BUF_SIZE; i++) {
29 if (buf[i] != val) {
30 fprintf(stderr, "got %d, wanted %d\n", buf[i], val);
31 return 1;
32 }
33 }
34
35 return 0;
36 }
37
test(const char * filename,int dio,int async)38 static int test(const char *filename, int dio, int async)
39 {
40 struct io_uring_buf_reg reg = { };
41 struct io_uring_sqe *sqe;
42 struct io_uring_cqe *cqe;
43 struct io_uring ring;
44 struct io_uring_buf_ring *br;
45 int ret, fd, i;
46 char *buf;
47 void *ptr;
48
49 ret = io_uring_queue_init(NR_BUFS, &ring, 0);
50 if (ret) {
51 fprintf(stderr, "ring setup failed: %d\n", ret);
52 return 1;
53 }
54
55 if (dio)
56 fd = open(filename, O_DIRECT | O_RDONLY);
57 else
58 fd = open(filename, O_RDONLY);
59 if (fd < 0) {
60 perror("open");
61 return 1;
62 }
63
64 posix_fadvise(fd, 0, FSIZE, POSIX_FADV_DONTNEED);
65
66 if (posix_memalign((void **) &buf, 4096, FSIZE))
67 return 1;
68 if (posix_memalign((void **) &br, 4096, 4096))
69 return 1;
70
71 reg.ring_addr = (unsigned long) br;
72 reg.ring_entries = NR_BUFS;
73 reg.bgid = 1;
74
75 ret = io_uring_register_buf_ring(&ring, ®, 0);
76 if (ret) {
77 if (ret == -EINVAL) {
78 no_buf_ring = 1;
79 return 0;
80 }
81 fprintf(stderr, "Buffer ring register failed %d\n", ret);
82 return 1;
83 }
84
85 ptr = buf;
86 for (i = 0; i < NR_BUFS; i++) {
87 io_uring_buf_ring_add(br, ptr, BUF_SIZE, i + 1, BR_MASK, i);
88 ptr += BUF_SIZE;
89 }
90 io_uring_buf_ring_advance(br, NR_BUFS);
91
92 for (i = 0; i < NR_BUFS; i++) {
93 sqe = io_uring_get_sqe(&ring);
94 io_uring_prep_read(sqe, fd, NULL, BUF_SIZE, i * BUF_SIZE);
95 sqe->buf_group = 1;
96 sqe->flags |= IOSQE_BUFFER_SELECT;
97 if (async && !(i & 1))
98 sqe->flags |= IOSQE_ASYNC;
99 sqe->user_data = i + 1;
100 }
101
102 ret = io_uring_submit(&ring);
103 if (ret != NR_BUFS) {
104 fprintf(stderr, "submit: %d\n", ret);
105 return 1;
106 }
107
108 for (i = 0; i < NR_BUFS; i++) {
109 int bid, ud;
110
111 ret = io_uring_wait_cqe(&ring, &cqe);
112 if (ret) {
113 fprintf(stderr, "wait cqe failed %d\n", ret);
114 return 1;
115 }
116 if (cqe->res != BUF_SIZE) {
117 fprintf(stderr, "cqe res %d\n", cqe->res);
118 return 1;
119 }
120 if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
121 fprintf(stderr, "no buffer selected\n");
122 return 1;
123 }
124 bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
125 ud = cqe->user_data;
126 io_uring_cqe_seen(&ring, cqe);
127 if (verify_buffer(buf + ((bid - 1) * BUF_SIZE), ud))
128 return 1;
129 }
130
131 return 0;
132 }
133
main(int argc,char * argv[])134 int main(int argc, char *argv[])
135 {
136 char buf[BUF_SIZE];
137 char fname[80];
138 int ret, fd, i, do_unlink;
139
140 if (argc > 1) {
141 strcpy(fname, argv[1]);
142 do_unlink = 0;
143 } else {
144 sprintf(fname, ".ringbuf-read.%d", getpid());
145 t_create_file(fname, FSIZE);
146 do_unlink = 1;
147 }
148
149 fd = open(fname, O_WRONLY);
150 if (fd < 0) {
151 perror("open");
152 goto err;
153 }
154 for (i = 0; i < NR_BUFS; i++) {
155 memset(buf, i + 1, BUF_SIZE);
156 ret = write(fd, buf, BUF_SIZE);
157 if (ret != BUF_SIZE) {
158 fprintf(stderr, "bad file prep write\n");
159 goto err;
160 }
161 }
162 close(fd);
163
164 ret = test(fname, 1, 0);
165 if (ret) {
166 fprintf(stderr, "dio test failed\n");
167 return ret;
168 }
169 if (no_buf_ring)
170 return 0;
171
172 ret = test(fname, 0, 0);
173 if (ret) {
174 fprintf(stderr, "buffered test failed\n");
175 return ret;
176 }
177
178 ret = test(fname, 1, 1);
179 if (ret) {
180 fprintf(stderr, "dio async test failed\n");
181 return ret;
182 }
183
184 ret = test(fname, 0, 1);
185 if (ret) {
186 fprintf(stderr, "buffered async test failed\n");
187 return ret;
188 }
189
190 return 0;
191 err:
192 if (do_unlink)
193 unlink(fname);
194 return 1;
195 }
196