#include #include #include #include #include #include #include #include "liburing.h" static int no_splice = 0; static int copy_single(struct io_uring *ring, int fd_in, loff_t off_in, int fd_out, loff_t off_out, int pipe_fds[2], unsigned int len, unsigned flags1, unsigned flags2) { struct io_uring_cqe *cqe; struct io_uring_sqe *sqe; int i, ret = -1; sqe = io_uring_get_sqe(ring); if (!sqe) { fprintf(stderr, "get sqe failed\n"); return -1; } io_uring_prep_splice(sqe, fd_in, off_in, pipe_fds[1], -1, len, flags1); sqe->flags = IOSQE_IO_LINK; sqe = io_uring_get_sqe(ring); if (!sqe) { fprintf(stderr, "get sqe failed\n"); return -1; } io_uring_prep_splice(sqe, pipe_fds[0], -1, fd_out, off_out, len, flags2); ret = io_uring_submit(ring); if (ret < 2) { /* submitted just one, kernel likely doesn't support splice */ if (!io_uring_peek_cqe(ring, &cqe) && cqe->res == -EINVAL) { no_splice = 1; return -1; } fprintf(stderr, "sqe submit failed: %d\n", ret); return -1; } for (i = 0; i < 2; i++) { ret = io_uring_wait_cqe(ring, &cqe); if (ret < 0) { fprintf(stderr, "wait completion %d\n", cqe->res); return ret; } ret = cqe->res; if (ret != len) { fprintf(stderr, "splice: returned %i, expected %i\n", cqe->res, len); return ret < 0 ? ret : -1; } io_uring_cqe_seen(ring, cqe); } return 0; } static int test_splice(struct io_uring *ring) { int ret = -1, len = 4 * 4096; int fd_out = -1, fd_in = -1; int pipe_fds[2] = {-1, -1}; if (pipe(pipe_fds) < 0) goto exit; fd_in = open("/dev/urandom", O_RDONLY); if (fd_in < 0) goto exit; fd_out = open(".splice_fd_out", O_CREAT | O_WRONLY, 0644); if (fd_out < 0) goto exit; if (ftruncate(fd_out, len) == -1) goto exit; ret = copy_single(ring, fd_in, -1, fd_out, -1, pipe_fds, len, SPLICE_F_MOVE | SPLICE_F_MORE, 0); if (ret == -EINVAL) { no_splice = 1; goto exit; } if (ret) { fprintf(stderr, "basic splice-copy failed\n"); goto exit; } ret = copy_single(ring, fd_in, 0, fd_out, 0, pipe_fds, len, 0, SPLICE_F_MOVE | SPLICE_F_MORE); if (ret) { fprintf(stderr, "basic splice with offset failed\n"); goto exit; } ret = io_uring_register_files(ring, &fd_in, 1); if (ret) { fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret); goto exit; } ret = copy_single(ring, 0, 0, fd_out, 0, pipe_fds, len, SPLICE_F_FD_IN_FIXED, 0); if (ret) { fprintf(stderr, "basic splice with reg files failed\n"); goto exit; } ret = 0; exit: if (fd_out >= 0) { unlink(".splice_fd_out"); close(fd_out); } if (fd_in >= 0) close(fd_in); if (pipe_fds[0] >= 0) { close(pipe_fds[0]); close(pipe_fds[1]); } return ret; } int main(int argc, char *argv[]) { struct io_uring ring; int ret; ret = io_uring_queue_init(8, &ring, 0); if (ret) { fprintf(stderr, "ring setup failed\n"); return 1; } ret = test_splice(&ring); if (ret && no_splice) { fprintf(stdout, "skip, doesn't support splice()\n"); return 0; } if (ret) { fprintf(stderr, "test_splice failed %i %i\n", ret, errno); return ret; } return 0; }