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