1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test fd passing with MSG_RING
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 <pthread.h>
13
14 #include "liburing.h"
15 #include "helpers.h"
16
17 static int no_msg;
18 static int no_sparse;
19 static int no_fd_pass;
20
21 struct data {
22 pthread_t thread;
23 pthread_barrier_t barrier;
24 int ring_flags;
25 int ring_fd;
26 char buf[32];
27 };
28
thread_fn(void * __data)29 static void *thread_fn(void *__data)
30 {
31 struct io_uring_sqe *sqe;
32 struct io_uring_cqe *cqe;
33 struct data *d = __data;
34 struct io_uring ring;
35 int ret, fd = -1;
36
37 io_uring_queue_init(8, &ring, d->ring_flags);
38 ret = io_uring_register_files(&ring, &fd, 1);
39 if (ret) {
40 if (ret != -EINVAL && ret != -EBADF)
41 fprintf(stderr, "thread file register: %d\n", ret);
42 no_sparse = 1;
43 pthread_barrier_wait(&d->barrier);
44 return NULL;
45 }
46
47 d->ring_fd = ring.ring_fd;
48 pthread_barrier_wait(&d->barrier);
49
50 /* wait for MSG */
51 ret = io_uring_wait_cqe(&ring, &cqe);
52 if (ret) {
53 fprintf(stderr, "wait_cqe dst: %d\n", ret);
54 return NULL;
55 }
56 if (cqe->res < 0) {
57 fprintf(stderr, "cqe error dst: %d\n", cqe->res);
58 return NULL;
59 }
60
61 fd = cqe->res;
62 io_uring_cqe_seen(&ring, cqe);
63 sqe = io_uring_get_sqe(&ring);
64 io_uring_prep_read(sqe, fd, d->buf, sizeof(d->buf), 0);
65 sqe->flags |= IOSQE_FIXED_FILE;
66 io_uring_submit(&ring);
67
68 ret = io_uring_wait_cqe(&ring, &cqe);
69 if (ret) {
70 fprintf(stderr, "wait_cqe dst: %d\n", ret);
71 return NULL;
72 }
73 if (cqe->res < 0) {
74 fprintf(stderr, "cqe error dst: %d\n", cqe->res);
75 return NULL;
76 }
77
78 io_uring_queue_exit(&ring);
79 return NULL;
80 }
81
test_remote(struct io_uring * src,int ring_flags)82 static int test_remote(struct io_uring *src, int ring_flags)
83 {
84 struct io_uring_sqe *sqe;
85 struct io_uring_cqe *cqe;
86 int fds[2], fd, ret;
87 struct data d;
88 char buf[32];
89 void *tret;
90 int i;
91
92 if (no_fd_pass)
93 return 0;
94
95 pthread_barrier_init(&d.barrier, NULL, 2);
96 d.ring_flags = ring_flags;
97 pthread_create(&d.thread, NULL, thread_fn, &d);
98 pthread_barrier_wait(&d.barrier);
99 memset(d.buf, 0, sizeof(d.buf));
100
101 if (no_sparse)
102 return 0;
103
104 if (pipe(fds) < 0) {
105 perror("pipe");
106 return 1;
107 }
108
109 fd = fds[0];
110 ret = io_uring_register_files(src, &fd, 1);
111 if (ret) {
112 fprintf(stderr, "register files failed: %d\n", ret);
113 return 1;
114 }
115
116 for (i = 0; i < ARRAY_SIZE(buf); i++)
117 buf[i] = rand();
118
119 sqe = io_uring_get_sqe(src);
120 io_uring_prep_write(sqe, fds[1], buf, sizeof(buf), 0);
121 sqe->user_data = 1;
122
123 sqe = io_uring_get_sqe(src);
124 io_uring_prep_msg_ring_fd(sqe, d.ring_fd, 0, 0, 0, 0);
125 sqe->user_data = 2;
126
127 io_uring_submit(src);
128
129 for (i = 0; i < 2; i++) {
130 ret = io_uring_wait_cqe(src, &cqe);
131 if (ret) {
132 fprintf(stderr, "wait_cqe: %d\n", ret);
133 return 1;
134 }
135 if (cqe->res < 0) {
136 fprintf(stderr, "cqe res %d\n", cqe->res);
137 return 1;
138 }
139 if (cqe->user_data == 1 && cqe->res != sizeof(buf)) {
140 fprintf(stderr, "short write %d\n", cqe->res);
141 return 1;
142 }
143 io_uring_cqe_seen(src, cqe);
144 }
145
146 pthread_join(d.thread, &tret);
147
148 if (memcmp(buf, d.buf, sizeof(buf))) {
149 fprintf(stderr, "buffers differ\n");
150 return 1;
151 }
152
153 close(fds[0]);
154 close(fds[1]);
155 io_uring_unregister_files(src);
156 return 0;
157 }
158
test_local(struct io_uring * src,struct io_uring * dst)159 static int test_local(struct io_uring *src, struct io_uring *dst)
160 {
161 struct io_uring_sqe *sqe;
162 struct io_uring_cqe *cqe;
163 int fds[2], fd, ret;
164 char buf[32], dst_buf[32];
165 int i;
166
167 if (no_fd_pass)
168 return 0;
169
170 fd = -1;
171 ret = io_uring_register_files(dst, &fd, 1);
172 if (ret) {
173 if (ret == -EBADF || ret == -EINVAL)
174 return 0;
175 fprintf(stderr, "register files failed: %d\n", ret);
176 return 1;
177 }
178
179 if (pipe(fds) < 0) {
180 perror("pipe");
181 return 1;
182 }
183
184 fd = fds[0];
185 ret = io_uring_register_files(src, &fd, 1);
186 if (ret) {
187 fprintf(stderr, "register files failed: %d\n", ret);
188 return 1;
189 }
190
191 memset(dst_buf, 0, sizeof(dst_buf));
192 for (i = 0; i < ARRAY_SIZE(buf); i++)
193 buf[i] = rand();
194
195 sqe = io_uring_get_sqe(src);
196 io_uring_prep_write(sqe, fds[1], buf, sizeof(buf), 0);
197 sqe->user_data = 1;
198
199 sqe = io_uring_get_sqe(src);
200 io_uring_prep_msg_ring_fd(sqe, dst->ring_fd, 0, 0, 10, 0);
201 sqe->user_data = 2;
202
203 io_uring_submit(src);
204
205 fd = -1;
206 for (i = 0; i < 2; i++) {
207 ret = io_uring_wait_cqe(src, &cqe);
208 if (ret) {
209 fprintf(stderr, "wait_cqe: %d\n", ret);
210 return 1;
211 }
212 if (cqe->user_data == 2 && cqe->res == -EINVAL) {
213 no_fd_pass = 1;
214 } else if (cqe->res < 0) {
215 fprintf(stderr, "cqe res %d\n", cqe->res);
216 return 1;
217 }
218 if (cqe->user_data == 1 && cqe->res != sizeof(buf)) {
219 fprintf(stderr, "short write %d\n", cqe->res);
220 return 1;
221 }
222 io_uring_cqe_seen(src, cqe);
223 }
224
225 if (no_fd_pass)
226 goto out;
227
228 ret = io_uring_wait_cqe(dst, &cqe);
229 if (ret) {
230 fprintf(stderr, "wait_cqe dst: %d\n", ret);
231 return 1;
232 }
233 if (cqe->res < 0) {
234 fprintf(stderr, "cqe error dst: %d\n", cqe->res);
235 return 1;
236 }
237
238 fd = cqe->res;
239 io_uring_cqe_seen(dst, cqe);
240 sqe = io_uring_get_sqe(dst);
241 io_uring_prep_read(sqe, fd, dst_buf, sizeof(dst_buf), 0);
242 sqe->flags |= IOSQE_FIXED_FILE;
243 sqe->user_data = 3;
244 io_uring_submit(dst);
245
246 ret = io_uring_wait_cqe(dst, &cqe);
247 if (ret) {
248 fprintf(stderr, "wait_cqe dst: %d\n", ret);
249 return 1;
250 }
251 if (cqe->res < 0) {
252 fprintf(stderr, "cqe error dst: %d\n", cqe->res);
253 return 1;
254 }
255 if (cqe->res != sizeof(dst_buf)) {
256 fprintf(stderr, "short read %d\n", cqe->res);
257 return 1;
258 }
259 if (memcmp(buf, dst_buf, sizeof(buf))) {
260 fprintf(stderr, "buffers differ\n");
261 return 1;
262 }
263
264 out:
265 close(fds[0]);
266 close(fds[1]);
267 io_uring_unregister_files(src);
268 io_uring_unregister_files(dst);
269 return 0;
270 }
271
test(int ring_flags)272 static int test(int ring_flags)
273 {
274 struct io_uring ring, ring2;
275 int ret;
276
277 ret = io_uring_queue_init(8, &ring, ring_flags);
278 if (ret) {
279 if (ret == -EINVAL)
280 return 0;
281 fprintf(stderr, "ring setup failed: %d\n", ret);
282 return T_EXIT_FAIL;
283 }
284 ret = io_uring_queue_init(8, &ring2, ring_flags);
285 if (ret) {
286 fprintf(stderr, "ring setup failed: %d\n", ret);
287 return T_EXIT_FAIL;
288 }
289
290 ret = test_local(&ring, &ring2);
291 if (ret) {
292 fprintf(stderr, "test local failed\n");
293 return T_EXIT_FAIL;
294 }
295 if (no_msg)
296 return T_EXIT_SKIP;
297
298 ret = test_remote(&ring, ring_flags);
299 if (ret) {
300 fprintf(stderr, "test_remote failed\n");
301 return T_EXIT_FAIL;
302 }
303
304 io_uring_queue_exit(&ring);
305 io_uring_queue_exit(&ring2);
306 return T_EXIT_PASS;
307 }
308
main(int argc,char * argv[])309 int main(int argc, char *argv[])
310 {
311 int ret;
312
313 if (argc > 1)
314 return T_EXIT_SKIP;
315
316 ret = test(0);
317 if (ret != T_EXIT_PASS) {
318 fprintf(stderr, "ring flags 0 failed\n");
319 return ret;
320 }
321 if (no_msg)
322 return T_EXIT_SKIP;
323
324 ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN);
325 if (ret != T_EXIT_PASS) {
326 fprintf(stderr, "ring flags defer failed\n");
327 return ret;
328 }
329
330 return ret;
331 }
332