• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <errno.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <fcntl.h>
7 #include <sys/mman.h>
8 
9 #include "helpers.h"
10 #include "liburing.h"
11 
12 #define BUF_SIZE (16 * 4096)
13 
14 struct test_ctx {
15 	int real_pipe1[2];
16 	int real_pipe2[2];
17 	int real_fd_in;
18 	int real_fd_out;
19 
20 	/* fds or for registered files */
21 	int pipe1[2];
22 	int pipe2[2];
23 	int fd_in;
24 	int fd_out;
25 
26 	void *buf_in;
27 	void *buf_out;
28 };
29 
30 static unsigned int splice_flags = 0;
31 static unsigned int sqe_flags = 0;
32 static int has_splice = 0;
33 static int has_tee = 0;
34 
read_buf(int fd,void * buf,int len)35 static int read_buf(int fd, void *buf, int len)
36 {
37 	int ret;
38 
39 	while (len) {
40 		ret = read(fd, buf, len);
41 		if (ret < 0)
42 			return ret;
43 		len -= ret;
44 		buf += ret;
45 	}
46 	return 0;
47 }
48 
write_buf(int fd,const void * buf,int len)49 static int write_buf(int fd, const void *buf, int len)
50 {
51 	int ret;
52 
53 	while (len) {
54 		ret = write(fd, buf, len);
55 		if (ret < 0)
56 			return ret;
57 		len -= ret;
58 		buf += ret;
59 	}
60 	return 0;
61 }
62 
check_content(int fd,void * buf,int len,const void * src)63 static int check_content(int fd, void *buf, int len, const void *src)
64 {
65 	int ret;
66 
67 	ret = read_buf(fd, buf, len);
68 	if (ret)
69 		return ret;
70 
71 	ret = memcmp(buf, src, len);
72 	return (ret != 0) ? -1 : 0;
73 }
74 
create_file(const char * filename)75 static int create_file(const char *filename)
76 {
77 	int fd, save_errno;
78 
79 	fd = open(filename, O_RDWR | O_CREAT, 0644);
80 	save_errno = errno;
81 	unlink(filename);
82 	errno = save_errno;
83 	return fd;
84 }
85 
init_splice_ctx(struct test_ctx * ctx)86 static int init_splice_ctx(struct test_ctx *ctx)
87 {
88 	int ret, rnd_fd;
89 
90 	ctx->buf_in = t_calloc(BUF_SIZE, 1);
91 	ctx->buf_out = t_calloc(BUF_SIZE, 1);
92 
93 	ctx->fd_in = create_file(".splice-test-in");
94 	if (ctx->fd_in < 0) {
95 		perror("file open");
96 		return 1;
97 	}
98 
99 	ctx->fd_out = create_file(".splice-test-out");
100 	if (ctx->fd_out < 0) {
101 		perror("file open");
102 		return 1;
103 	}
104 
105 	/* get random data */
106 	rnd_fd = open("/dev/urandom", O_RDONLY);
107 	if (rnd_fd < 0)
108 		return 1;
109 
110 	ret = read_buf(rnd_fd, ctx->buf_in, BUF_SIZE);
111 	if (ret != 0)
112 		return 1;
113 	close(rnd_fd);
114 
115 	/* populate file */
116 	ret = write_buf(ctx->fd_in, ctx->buf_in, BUF_SIZE);
117 	if (ret)
118 		return ret;
119 
120 	if (pipe(ctx->pipe1) < 0)
121 		return 1;
122 	if (pipe(ctx->pipe2) < 0)
123 		return 1;
124 
125 	ctx->real_pipe1[0] = ctx->pipe1[0];
126 	ctx->real_pipe1[1] = ctx->pipe1[1];
127 	ctx->real_pipe2[0] = ctx->pipe2[0];
128 	ctx->real_pipe2[1] = ctx->pipe2[1];
129 	ctx->real_fd_in = ctx->fd_in;
130 	ctx->real_fd_out = ctx->fd_out;
131 	return 0;
132 }
133 
do_splice_op(struct io_uring * ring,int fd_in,loff_t off_in,int fd_out,loff_t off_out,unsigned int len,__u8 opcode)134 static int do_splice_op(struct io_uring *ring,
135 			int fd_in, loff_t off_in,
136 			int fd_out, loff_t off_out,
137 			unsigned int len,
138 			__u8 opcode)
139 {
140 	struct io_uring_cqe *cqe;
141 	struct io_uring_sqe *sqe;
142 	int ret = -1;
143 
144 	do {
145 		sqe = io_uring_get_sqe(ring);
146 		if (!sqe) {
147 			fprintf(stderr, "get sqe failed\n");
148 			return -1;
149 		}
150 		io_uring_prep_splice(sqe, fd_in, off_in, fd_out, off_out,
151 				     len, splice_flags);
152 		sqe->flags |= sqe_flags;
153 		sqe->user_data = 42;
154 		sqe->opcode = opcode;
155 
156 		ret = io_uring_submit(ring);
157 		if (ret != 1) {
158 			fprintf(stderr, "sqe submit failed: %d\n", ret);
159 			return ret;
160 		}
161 
162 		ret = io_uring_wait_cqe(ring, &cqe);
163 		if (ret < 0) {
164 			fprintf(stderr, "wait completion %d\n", cqe->res);
165 			return ret;
166 		}
167 
168 		if (cqe->res <= 0) {
169 			io_uring_cqe_seen(ring, cqe);
170 			return cqe->res;
171 		}
172 
173 		len -= cqe->res;
174 		if (off_in != -1)
175 			off_in += cqe->res;
176 		if (off_out != -1)
177 			off_out += cqe->res;
178 		io_uring_cqe_seen(ring, cqe);
179 	} while (len);
180 
181 	return 0;
182 }
183 
do_splice(struct io_uring * ring,int fd_in,loff_t off_in,int fd_out,loff_t off_out,unsigned int len)184 static int do_splice(struct io_uring *ring,
185 			int fd_in, loff_t off_in,
186 			int fd_out, loff_t off_out,
187 			unsigned int len)
188 {
189 	return do_splice_op(ring, fd_in, off_in, fd_out, off_out, len,
190 			    IORING_OP_SPLICE);
191 }
192 
do_tee(struct io_uring * ring,int fd_in,int fd_out,unsigned int len)193 static int do_tee(struct io_uring *ring, int fd_in, int fd_out,
194 		  unsigned int len)
195 {
196 	return do_splice_op(ring, fd_in, 0, fd_out, 0, len, IORING_OP_TEE);
197 }
198 
check_splice_support(struct io_uring * ring,struct test_ctx * ctx)199 static void check_splice_support(struct io_uring *ring, struct test_ctx *ctx)
200 {
201 	int ret;
202 
203 	ret = do_splice(ring, -1, 0, -1, 0, BUF_SIZE);
204 	has_splice = (ret == -EBADF);
205 }
206 
check_tee_support(struct io_uring * ring,struct test_ctx * ctx)207 static void check_tee_support(struct io_uring *ring, struct test_ctx *ctx)
208 {
209 	int ret;
210 
211 	ret = do_tee(ring, -1, -1, BUF_SIZE);
212 	has_tee = (ret == -EBADF);
213 }
214 
check_zero_splice(struct io_uring * ring,struct test_ctx * ctx)215 static int check_zero_splice(struct io_uring *ring, struct test_ctx *ctx)
216 {
217 	int ret;
218 
219 	ret = do_splice(ring, ctx->fd_in, -1, ctx->pipe1[1], -1, 0);
220 	if (ret)
221 		return ret;
222 
223 	ret = do_splice(ring, ctx->pipe2[0], -1, ctx->pipe1[1], -1, 0);
224 	if (ret)
225 		return ret;
226 
227 	return 0;
228 }
229 
splice_to_pipe(struct io_uring * ring,struct test_ctx * ctx)230 static int splice_to_pipe(struct io_uring *ring, struct test_ctx *ctx)
231 {
232 	int ret;
233 
234 	ret = lseek(ctx->real_fd_in, 0, SEEK_SET);
235 	if (ret)
236 		return ret;
237 
238 	/* implicit file offset */
239 	ret = do_splice(ring, ctx->fd_in, -1, ctx->pipe1[1], -1, BUF_SIZE);
240 	if (ret)
241 		return ret;
242 
243 	ret = check_content(ctx->real_pipe1[0], ctx->buf_out, BUF_SIZE,
244 			     ctx->buf_in);
245 	if (ret)
246 		return ret;
247 
248 	/* explicit file offset */
249 	ret = do_splice(ring, ctx->fd_in, 0, ctx->pipe1[1], -1, BUF_SIZE);
250 	if (ret)
251 		return ret;
252 
253 	return check_content(ctx->real_pipe1[0], ctx->buf_out, BUF_SIZE,
254 			     ctx->buf_in);
255 }
256 
splice_from_pipe(struct io_uring * ring,struct test_ctx * ctx)257 static int splice_from_pipe(struct io_uring *ring, struct test_ctx *ctx)
258 {
259 	int ret;
260 
261 	ret = write_buf(ctx->real_pipe1[1], ctx->buf_in, BUF_SIZE);
262 	if (ret)
263 		return ret;
264 	ret = do_splice(ring, ctx->pipe1[0], -1, ctx->fd_out, 0, BUF_SIZE);
265 	if (ret)
266 		return ret;
267 	ret = check_content(ctx->real_fd_out, ctx->buf_out, BUF_SIZE,
268 			     ctx->buf_in);
269 	if (ret)
270 		return ret;
271 
272 	ret = ftruncate(ctx->real_fd_out, 0);
273 	if (ret)
274 		return ret;
275 	return lseek(ctx->real_fd_out, 0, SEEK_SET);
276 }
277 
splice_pipe_to_pipe(struct io_uring * ring,struct test_ctx * ctx)278 static int splice_pipe_to_pipe(struct io_uring *ring, struct test_ctx *ctx)
279 {
280 	int ret;
281 
282 	ret = do_splice(ring, ctx->fd_in, 0, ctx->pipe1[1], -1, BUF_SIZE);
283 	if (ret)
284 		return ret;
285 	ret = do_splice(ring, ctx->pipe1[0], -1, ctx->pipe2[1], -1, BUF_SIZE);
286 	if (ret)
287 		return ret;
288 
289 	return check_content(ctx->real_pipe2[0], ctx->buf_out, BUF_SIZE,
290 				ctx->buf_in);
291 }
292 
fail_splice_pipe_offset(struct io_uring * ring,struct test_ctx * ctx)293 static int fail_splice_pipe_offset(struct io_uring *ring, struct test_ctx *ctx)
294 {
295 	int ret;
296 
297 	ret = do_splice(ring, ctx->fd_in, 0, ctx->pipe1[1], 0, BUF_SIZE);
298 	if (ret != -ESPIPE && ret != -EINVAL)
299 		return ret;
300 
301 	ret = do_splice(ring, ctx->pipe1[0], 0, ctx->fd_out, 0, BUF_SIZE);
302 	if (ret != -ESPIPE && ret != -EINVAL)
303 		return ret;
304 
305 	return 0;
306 }
307 
fail_tee_nonpipe(struct io_uring * ring,struct test_ctx * ctx)308 static int fail_tee_nonpipe(struct io_uring *ring, struct test_ctx *ctx)
309 {
310 	int ret;
311 
312 	ret = do_tee(ring, ctx->fd_in, ctx->pipe1[1], BUF_SIZE);
313 	if (ret != -ESPIPE && ret != -EINVAL)
314 		return ret;
315 
316 	return 0;
317 }
318 
fail_tee_offset(struct io_uring * ring,struct test_ctx * ctx)319 static int fail_tee_offset(struct io_uring *ring, struct test_ctx *ctx)
320 {
321 	int ret;
322 
323 	ret = do_splice_op(ring, ctx->pipe2[0], -1, ctx->pipe1[1], 0,
324 			   BUF_SIZE, IORING_OP_TEE);
325 	if (ret != -ESPIPE && ret != -EINVAL)
326 		return ret;
327 
328 	ret = do_splice_op(ring, ctx->pipe2[0], 0, ctx->pipe1[1], -1,
329 			   BUF_SIZE, IORING_OP_TEE);
330 	if (ret != -ESPIPE && ret != -EINVAL)
331 		return ret;
332 
333 	return 0;
334 }
335 
check_tee(struct io_uring * ring,struct test_ctx * ctx)336 static int check_tee(struct io_uring *ring, struct test_ctx *ctx)
337 {
338 	int ret;
339 
340 	ret = write_buf(ctx->real_pipe1[1], ctx->buf_in, BUF_SIZE);
341 	if (ret)
342 		return ret;
343 	ret = do_tee(ring, ctx->pipe1[0], ctx->pipe2[1], BUF_SIZE);
344 	if (ret)
345 		return ret;
346 
347 	ret = check_content(ctx->real_pipe1[0], ctx->buf_out, BUF_SIZE,
348 				ctx->buf_in);
349 	if (ret) {
350 		fprintf(stderr, "tee(), invalid src data\n");
351 		return ret;
352 	}
353 
354 	ret = check_content(ctx->real_pipe2[0], ctx->buf_out, BUF_SIZE,
355 				ctx->buf_in);
356 	if (ret) {
357 		fprintf(stderr, "tee(), invalid dst data\n");
358 		return ret;
359 	}
360 
361 	return 0;
362 }
363 
check_zero_tee(struct io_uring * ring,struct test_ctx * ctx)364 static int check_zero_tee(struct io_uring *ring, struct test_ctx *ctx)
365 {
366 	return do_tee(ring, ctx->pipe2[0], ctx->pipe1[1], 0);
367 }
368 
test_splice(struct io_uring * ring,struct test_ctx * ctx)369 static int test_splice(struct io_uring *ring, struct test_ctx *ctx)
370 {
371 	int ret;
372 
373 	if (has_splice) {
374 		ret = check_zero_splice(ring, ctx);
375 		if (ret) {
376 			fprintf(stderr, "check_zero_splice failed %i %i\n",
377 				ret, errno);
378 			return ret;
379 		}
380 
381 		ret = splice_to_pipe(ring, ctx);
382 		if (ret) {
383 			fprintf(stderr, "splice_to_pipe failed %i %i\n",
384 				ret, errno);
385 			return ret;
386 		}
387 
388 		ret = splice_from_pipe(ring, ctx);
389 		if (ret) {
390 			fprintf(stderr, "splice_from_pipe failed %i %i\n",
391 				ret, errno);
392 			return ret;
393 		}
394 
395 		ret = splice_pipe_to_pipe(ring, ctx);
396 		if (ret) {
397 			fprintf(stderr, "splice_pipe_to_pipe failed %i %i\n",
398 				ret, errno);
399 			return ret;
400 		}
401 
402 		ret = fail_splice_pipe_offset(ring, ctx);
403 		if (ret) {
404 			fprintf(stderr, "fail_splice_pipe_offset failed %i %i\n",
405 				ret, errno);
406 			return ret;
407 		}
408 	}
409 
410 	if (has_tee) {
411 		ret = check_zero_tee(ring, ctx);
412 		if (ret) {
413 			fprintf(stderr, "check_zero_tee() failed %i %i\n",
414 				ret, errno);
415 			return ret;
416 		}
417 
418 		ret = fail_tee_nonpipe(ring, ctx);
419 		if (ret) {
420 			fprintf(stderr, "fail_tee_nonpipe() failed %i %i\n",
421 				ret, errno);
422 			return ret;
423 		}
424 
425 		ret = fail_tee_offset(ring, ctx);
426 		if (ret) {
427 			fprintf(stderr, "fail_tee_offset failed %i %i\n",
428 				ret, errno);
429 			return ret;
430 		}
431 
432 		ret = check_tee(ring, ctx);
433 		if (ret) {
434 			fprintf(stderr, "check_tee() failed %i %i\n",
435 				ret, errno);
436 			return ret;
437 		}
438 	}
439 
440 	return 0;
441 }
442 
main(int argc,char * argv[])443 int main(int argc, char *argv[])
444 {
445 	struct io_uring ring;
446 	struct io_uring_params p = { };
447 	struct test_ctx ctx;
448 	int ret;
449 	int reg_fds[6];
450 
451 	if (argc > 1)
452 		return 0;
453 
454 	ret = io_uring_queue_init_params(8, &ring, &p);
455 	if (ret) {
456 		fprintf(stderr, "ring setup failed\n");
457 		return 1;
458 	}
459 	if (!(p.features & IORING_FEAT_FAST_POLL)) {
460 		fprintf(stdout, "No splice support, skipping\n");
461 		return 0;
462 	}
463 
464 	ret = init_splice_ctx(&ctx);
465 	if (ret) {
466 		fprintf(stderr, "init failed %i %i\n", ret, errno);
467 		return 1;
468 	}
469 
470 	check_splice_support(&ring, &ctx);
471 	if (!has_splice)
472 		fprintf(stdout, "skip, doesn't support splice()\n");
473 	check_tee_support(&ring, &ctx);
474 	if (!has_tee)
475 		fprintf(stdout, "skip, doesn't support tee()\n");
476 
477 	ret = test_splice(&ring, &ctx);
478 	if (ret) {
479 		fprintf(stderr, "basic splice tests failed\n");
480 		return ret;
481 	}
482 
483 	reg_fds[0] = ctx.real_pipe1[0];
484 	reg_fds[1] = ctx.real_pipe1[1];
485 	reg_fds[2] = ctx.real_pipe2[0];
486 	reg_fds[3] = ctx.real_pipe2[1];
487 	reg_fds[4] = ctx.real_fd_in;
488 	reg_fds[5] = ctx.real_fd_out;
489 	ret = io_uring_register_files(&ring, reg_fds, 6);
490 	if (ret) {
491 		fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
492 		return 1;
493 	}
494 
495 	/* remap fds to registered */
496 	ctx.pipe1[0] = 0;
497 	ctx.pipe1[1] = 1;
498 	ctx.pipe2[0] = 2;
499 	ctx.pipe2[1] = 3;
500 	ctx.fd_in = 4;
501 	ctx.fd_out = 5;
502 
503 	splice_flags = SPLICE_F_FD_IN_FIXED;
504 	sqe_flags = IOSQE_FIXED_FILE;
505 	ret = test_splice(&ring, &ctx);
506 	if (ret) {
507 		fprintf(stderr, "registered fds splice tests failed\n");
508 		return ret;
509 	}
510 	return 0;
511 }
512