1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test buffer cloning between rings
4 *
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <sys/uio.h>
11
12 #include "liburing.h"
13 #include "helpers.h"
14
15 #define NR_VECS 64
16 #define BUF_SIZE 8192
17
18 static int no_buf_clone;
19
test(int reg_src,int reg_dst)20 static int test(int reg_src, int reg_dst)
21 {
22 struct iovec vecs[NR_VECS];
23 struct io_uring src, dst;
24 int ret, i;
25
26 ret = io_uring_queue_init(1, &src, 0);
27 if (ret) {
28 fprintf(stderr, "ring_init: %d\n", ret);
29 return T_EXIT_FAIL;
30 }
31 ret = io_uring_queue_init(1, &dst, 0);
32 if (ret) {
33 fprintf(stderr, "ring_init: %d\n", ret);
34 return T_EXIT_FAIL;
35 }
36 if (reg_src) {
37 ret = io_uring_register_ring_fd(&src);
38 if (ret < 0) {
39 if (ret == -EINVAL)
40 return T_EXIT_SKIP;
41 fprintf(stderr, "register ring: %d\n", ret);
42 return T_EXIT_FAIL;
43 }
44 }
45 if (reg_dst) {
46 ret = io_uring_register_ring_fd(&dst);
47 if (ret < 0) {
48 if (ret == -EINVAL)
49 return T_EXIT_SKIP;
50 fprintf(stderr, "register ring: %d\n", ret);
51 return T_EXIT_FAIL;
52 }
53 }
54
55 /* test fail with no buffers in src */
56 ret = io_uring_clone_buffers(&dst, &src);
57 if (ret == -EINVAL) {
58 /* no buffer copy support */
59 no_buf_clone = true;
60 return T_EXIT_SKIP;
61 } else if (ret != -ENXIO) {
62 fprintf(stderr, "empty copy: %d\n", ret);
63 return T_EXIT_FAIL;
64 }
65
66 for (i = 0; i < NR_VECS; i++) {
67 if (posix_memalign(&vecs[i].iov_base, 4096, BUF_SIZE))
68 return T_EXIT_FAIL;
69 vecs[i].iov_len = BUF_SIZE;
70 }
71
72 ret = io_uring_register_buffers(&src, vecs, NR_VECS);
73 if (ret < 0) {
74 if (ret == -ENOMEM)
75 return T_EXIT_SKIP;
76 return T_EXIT_FAIL;
77 }
78
79 /* copy should work now */
80 ret = io_uring_clone_buffers(&dst, &src);
81 if (ret) {
82 fprintf(stderr, "buffer copy: %d\n", ret);
83 return T_EXIT_FAIL;
84 }
85
86 /* try copy again, should get -EBUSY */
87 ret = io_uring_clone_buffers(&dst, &src);
88 if (ret != -EBUSY) {
89 fprintf(stderr, "busy copy: %d\n", ret);
90 return T_EXIT_FAIL;
91 }
92
93 ret = io_uring_unregister_buffers(&dst);
94 if (ret) {
95 fprintf(stderr, "dst unregister buffers: %d\n", ret);
96 return T_EXIT_FAIL;
97 }
98
99 ret = io_uring_unregister_buffers(&dst);
100 if (ret != -ENXIO) {
101 fprintf(stderr, "dst unregister empty buffers: %d\n", ret);
102 return T_EXIT_FAIL;
103 }
104
105 ret = io_uring_unregister_buffers(&src);
106 if (ret) {
107 fprintf(stderr, "src unregister buffers: %d\n", ret);
108 return T_EXIT_FAIL;
109 }
110
111 ret = io_uring_register_buffers(&dst, vecs, NR_VECS);
112 if (ret < 0) {
113 fprintf(stderr, "register buffers dst; %d\n", ret);
114 return T_EXIT_FAIL;
115 }
116
117 ret = io_uring_clone_buffers(&src, &dst);
118 if (ret) {
119 fprintf(stderr, "buffer copy reverse: %d\n", ret);
120 return T_EXIT_FAIL;
121 }
122
123 ret = io_uring_unregister_buffers(&dst);
124 if (ret) {
125 fprintf(stderr, "dst unregister buffers: %d\n", ret);
126 return T_EXIT_FAIL;
127 }
128
129 ret = io_uring_unregister_buffers(&dst);
130 if (ret != -ENXIO) {
131 fprintf(stderr, "dst unregister empty buffers: %d\n", ret);
132 return T_EXIT_FAIL;
133 }
134
135 ret = io_uring_unregister_buffers(&src);
136 if (ret) {
137 fprintf(stderr, "src unregister buffers: %d\n", ret);
138 return T_EXIT_FAIL;
139 }
140
141 io_uring_queue_exit(&src);
142 io_uring_queue_exit(&dst);
143
144 for (i = 0; i < NR_VECS; i++)
145 free(vecs[i].iov_base);
146
147 return T_EXIT_PASS;
148 }
149
test_dummy(void)150 static int test_dummy(void)
151 {
152 struct iovec vec = { };
153 struct io_uring src, dst;
154 int ret;
155
156 ret = io_uring_queue_init(1, &src, 0);
157 if (ret) {
158 fprintf(stderr, "ring_init: %d\n", ret);
159 return T_EXIT_FAIL;
160 }
161 ret = io_uring_queue_init(1, &dst, 0);
162 if (ret) {
163 fprintf(stderr, "ring_init: %d\n", ret);
164 return T_EXIT_FAIL;
165 }
166
167 ret = io_uring_register_buffers(&src, &vec, 1);
168 if (ret < 0) {
169 fprintf(stderr, "failed to register dummy buffer: %d\n", ret);
170 return T_EXIT_FAIL;
171 }
172
173 ret = io_uring_clone_buffers(&dst, &src);
174 if (ret) {
175 fprintf(stderr, "clone dummy buf: %d\n", ret);
176 return T_EXIT_FAIL;
177 }
178
179 ret = io_uring_unregister_buffers(&src);
180 if (ret) {
181 fprintf(stderr, "rsc unregister buffers: %d\n", ret);
182 return T_EXIT_FAIL;
183 }
184
185 ret = io_uring_unregister_buffers(&dst);
186 if (ret) {
187 fprintf(stderr, "dst unregister buffers: %d\n", ret);
188 return T_EXIT_FAIL;
189 }
190
191 io_uring_queue_exit(&src);
192 io_uring_queue_exit(&dst);
193
194 return T_EXIT_PASS;
195 }
196
main(int argc,char * argv[])197 int main(int argc, char *argv[])
198 {
199 int ret;
200
201 if (argc > 1)
202 return T_EXIT_SKIP;
203
204 ret = test(0, 0);
205 if (ret == T_EXIT_SKIP) {
206 return T_EXIT_SKIP;
207 } else if (ret != T_EXIT_PASS) {
208 fprintf(stderr, "test 0 0 failed\n");
209 return T_EXIT_FAIL;
210 }
211 if (no_buf_clone)
212 return T_EXIT_SKIP;
213
214 ret = test(0, 1);
215 if (ret == T_EXIT_SKIP) {
216 return T_EXIT_SKIP;
217 } else if (ret != T_EXIT_PASS) {
218 fprintf(stderr, "test 0 1 failed\n");
219 return T_EXIT_FAIL;
220 }
221
222 ret = test(1, 0);
223 if (ret == T_EXIT_SKIP) {
224 return T_EXIT_SKIP;
225 } else if (ret != T_EXIT_PASS) {
226 fprintf(stderr, "test 1 0 failed\n");
227 return T_EXIT_FAIL;
228 }
229
230 ret = test(1, 1);
231 if (ret == T_EXIT_SKIP) {
232 return T_EXIT_SKIP;
233 } else if (ret != T_EXIT_PASS) {
234 fprintf(stderr, "test 1 1 failed\n");
235 return T_EXIT_FAIL;
236 }
237
238 ret = test_dummy();
239 if (ret == T_EXIT_SKIP) {
240 return T_EXIT_SKIP;
241 } else if (ret != T_EXIT_PASS) {
242 fprintf(stderr, "test_dummy failed\n");
243 return T_EXIT_FAIL;
244 }
245
246 return T_EXIT_PASS;
247 }
248