/* SPDX-License-Identifier: MIT */ /* * Very basic proof-of-concept for doing a copy with linked SQEs. Needs a * bit of error handling and short read love. */ #include #include #include #include #include #include #include #include #include #include #include #include "liburing.h" #define QD 64 #define BS (32*1024) struct io_data { size_t offset; int index; struct iovec iov; }; static int infd, outfd; static unsigned inflight; static int setup_context(unsigned entries, struct io_uring *ring) { int ret; ret = io_uring_queue_init(entries, ring, 0); if (ret < 0) { fprintf(stderr, "queue_init: %s\n", strerror(-ret)); return -1; } return 0; } static int get_file_size(int fd, off_t *size) { struct stat st; if (fstat(fd, &st) < 0) return -1; if (S_ISREG(st.st_mode)) { *size = st.st_size; return 0; } else if (S_ISBLK(st.st_mode)) { unsigned long long bytes; if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) return -1; *size = bytes; return 0; } return -1; } static void queue_rw_pair(struct io_uring *ring, off_t size, off_t offset) { struct io_uring_sqe *sqe; struct io_data *data; void *ptr; ptr = malloc(size + sizeof(*data)); data = ptr + size; data->index = 0; data->offset = offset; data->iov.iov_base = ptr; data->iov.iov_len = size; sqe = io_uring_get_sqe(ring); io_uring_prep_readv(sqe, infd, &data->iov, 1, offset); sqe->flags |= IOSQE_IO_LINK; io_uring_sqe_set_data(sqe, data); sqe = io_uring_get_sqe(ring); io_uring_prep_writev(sqe, outfd, &data->iov, 1, offset); io_uring_sqe_set_data(sqe, data); } static int handle_cqe(struct io_uring *ring, struct io_uring_cqe *cqe) { struct io_data *data = io_uring_cqe_get_data(cqe); int ret = 0; data->index++; if (cqe->res < 0) { if (cqe->res == -ECANCELED) { queue_rw_pair(ring, BS, data->offset); inflight += 2; } else { printf("cqe error: %s\n", strerror(-cqe->res)); ret = 1; } } if (data->index == 2) { void *ptr = (void *) data - data->iov.iov_len; free(ptr); } io_uring_cqe_seen(ring, cqe); return ret; } static int copy_file(struct io_uring *ring, off_t insize) { struct io_uring_cqe *cqe; size_t this_size; off_t offset; offset = 0; while (insize) { int has_inflight = inflight; int depth; while (insize && inflight < QD) { this_size = BS; if (this_size > insize) this_size = insize; queue_rw_pair(ring, this_size, offset); offset += this_size; insize -= this_size; inflight += 2; } if (has_inflight != inflight) io_uring_submit(ring); if (insize) depth = QD; else depth = 1; while (inflight >= depth) { int ret; ret = io_uring_wait_cqe(ring, &cqe); if (ret < 0) { printf("wait cqe: %s\n", strerror(-ret)); return 1; } if (handle_cqe(ring, cqe)) return 1; inflight--; } } return 0; } int main(int argc, char *argv[]) { struct io_uring ring; off_t insize; int ret; if (argc < 3) { printf("%s: infile outfile\n", argv[0]); return 1; } infd = open(argv[1], O_RDONLY); if (infd < 0) { perror("open infile"); return 1; } outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); if (outfd < 0) { perror("open outfile"); return 1; } if (setup_context(QD, &ring)) return 1; if (get_file_size(infd, &insize)) return 1; ret = copy_file(&ring, insize); close(infd); close(outfd); io_uring_queue_exit(&ring); return ret; }