• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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