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_sqe *sqe;
41 struct io_uring_cqe *cqe;
42 struct io_uring ring;
43 struct io_uring_buf_ring *br;
44 int ret, fd, i;
45 char *buf;
46 void *ptr;
47
48 ret = io_uring_queue_init(NR_BUFS, &ring, 0);
49 if (ret) {
50 fprintf(stderr, "ring setup failed: %d\n", ret);
51 return 1;
52 }
53
54 if (dio) {
55 fd = open(filename, O_DIRECT | O_RDONLY);
56 if (fd < 0 && errno == EINVAL)
57 return T_EXIT_SKIP;
58 } else {
59 fd = open(filename, O_RDONLY);
60 }
61 if (fd < 0) {
62 if (errno == EPERM || errno == EACCES)
63 return T_EXIT_SKIP;
64 perror("open");
65 return 1;
66 }
67
68 posix_fadvise(fd, 0, FSIZE, POSIX_FADV_DONTNEED);
69
70 if (posix_memalign((void **) &buf, 4096, FSIZE))
71 return 1;
72
73 br = io_uring_setup_buf_ring(&ring, NR_BUFS, 1, 0, &ret);
74 if (!br) {
75 if (ret == -EINVAL) {
76 no_buf_ring = 1;
77 return 0;
78 }
79 fprintf(stderr, "Buffer ring register failed %d\n", ret);
80 return 1;
81 }
82
83 ptr = buf;
84 for (i = 0; i < NR_BUFS; i++) {
85 io_uring_buf_ring_add(br, ptr, BUF_SIZE, i + 1, BR_MASK, i);
86 ptr += BUF_SIZE;
87 }
88 io_uring_buf_ring_advance(br, NR_BUFS);
89
90 for (i = 0; i < NR_BUFS; i++) {
91 sqe = io_uring_get_sqe(&ring);
92 io_uring_prep_read(sqe, fd, NULL, BUF_SIZE, i * BUF_SIZE);
93 sqe->buf_group = 1;
94 sqe->flags |= IOSQE_BUFFER_SELECT;
95 if (async && !(i & 1))
96 sqe->flags |= IOSQE_ASYNC;
97 sqe->user_data = i + 1;
98 }
99
100 ret = io_uring_submit(&ring);
101 if (ret != NR_BUFS) {
102 fprintf(stderr, "submit: %d\n", ret);
103 return 1;
104 }
105
106 for (i = 0; i < NR_BUFS; i++) {
107 int bid, ud;
108
109 ret = io_uring_wait_cqe(&ring, &cqe);
110 if (ret) {
111 fprintf(stderr, "wait cqe failed %d\n", ret);
112 return 1;
113 }
114 if (cqe->res != BUF_SIZE) {
115 fprintf(stderr, "cqe res %d\n", cqe->res);
116 return 1;
117 }
118 if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
119 fprintf(stderr, "no buffer selected\n");
120 return 1;
121 }
122 bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
123 ud = cqe->user_data;
124 io_uring_cqe_seen(&ring, cqe);
125 if (verify_buffer(buf + ((bid - 1) * BUF_SIZE), ud))
126 return 1;
127 }
128 free(buf);
129
130 return 0;
131 }
132
main(int argc,char * argv[])133 int main(int argc, char *argv[])
134 {
135 char buf[BUF_SIZE];
136 char fname[80];
137 int ret, fd, i, do_unlink;
138
139 if (argc > 1) {
140 strcpy(fname, argv[1]);
141 do_unlink = 0;
142 } else {
143 sprintf(fname, ".ringbuf-read.%d", getpid());
144 t_create_file(fname, FSIZE);
145 do_unlink = 1;
146 }
147
148 fd = open(fname, O_WRONLY);
149 if (fd < 0) {
150 if (errno == EPERM || errno == EACCES)
151 return T_EXIT_SKIP;
152 perror("open");
153 goto err;
154 }
155 for (i = 0; i < NR_BUFS; i++) {
156 memset(buf, i + 1, BUF_SIZE);
157 ret = write(fd, buf, BUF_SIZE);
158 if (ret != BUF_SIZE) {
159 fprintf(stderr, "bad file prep write\n");
160 close(fd);
161 goto err;
162 }
163 }
164 close(fd);
165
166 ret = test(fname, 1, 0);
167 if (ret == T_EXIT_FAIL) {
168 fprintf(stderr, "dio test failed\n");
169 goto err;
170 }
171 if (no_buf_ring)
172 goto pass;
173
174 ret = test(fname, 0, 0);
175 if (ret) {
176 fprintf(stderr, "buffered test failed\n");
177 goto err;
178 }
179
180 ret = test(fname, 1, 1);
181 if (ret == T_EXIT_FAIL) {
182 fprintf(stderr, "dio async test failed\n");
183 goto err;
184 }
185
186 ret = test(fname, 0, 1);
187 if (ret == T_EXIT_FAIL) {
188 fprintf(stderr, "buffered async test failed\n");
189 goto err;
190 }
191
192 pass:
193 ret = T_EXIT_PASS;
194 goto out;
195 err:
196 ret = T_EXIT_FAIL;
197 out:
198 if (do_unlink)
199 unlink(fname);
200 return ret;
201 }
202