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