1 // SPDX-License-Identifier: GPL-2.0
3 * Simple test program that demonstrates a file copy through io_uring. This
4 * uses the API exposed by liburing.
6 * Copyright (C) 2018-2019 Jens Axboe
17 #include <sys/ioctl.h>
24 static int infd, outfd;
28 off_t first_offset, offset;
33 static int setup_context(unsigned entries, struct io_uring *ring)
37 ret = io_uring_queue_init(entries, ring, 0);
39 fprintf(stderr, "queue_init: %s\n", strerror(-ret));
46 static int get_file_size(int fd, off_t *size)
50 if (fstat(fd, &st) < 0)
52 if (S_ISREG(st.st_mode)) {
55 } else if (S_ISBLK(st.st_mode)) {
56 unsigned long long bytes;
58 if (ioctl(fd, BLKGETSIZE64, &bytes) != 0)
68 static void queue_prepped(struct io_uring *ring, struct io_data *data)
70 struct io_uring_sqe *sqe;
72 sqe = io_uring_get_sqe(ring);
76 io_uring_prep_readv(sqe, infd, &data->iov, 1, data->offset);
78 io_uring_prep_writev(sqe, outfd, &data->iov, 1, data->offset);
80 io_uring_sqe_set_data(sqe, data);
83 static int queue_read(struct io_uring *ring, off_t size, off_t offset)
85 struct io_uring_sqe *sqe;
88 sqe = io_uring_get_sqe(ring);
92 data = malloc(size + sizeof(*data));
94 data->offset = data->first_offset = offset;
96 data->iov.iov_base = data + 1;
97 data->iov.iov_len = size;
98 data->first_len = size;
100 io_uring_prep_readv(sqe, infd, &data->iov, 1, offset);
101 io_uring_sqe_set_data(sqe, data);
105 static void queue_write(struct io_uring *ring, struct io_data *data)
108 data->offset = data->first_offset;
110 data->iov.iov_base = data + 1;
111 data->iov.iov_len = data->first_len;
113 queue_prepped(ring, data);
114 io_uring_submit(ring);
117 static int copy_file(struct io_uring *ring, off_t insize)
119 unsigned long reads, writes;
120 struct io_uring_cqe *cqe;
121 off_t write_left, offset;
125 writes = reads = offset = 0;
127 while (insize || write_left) {
128 unsigned long had_reads;
132 * Queue up as many reads as we can
136 off_t this_size = insize;
138 if (reads + writes >= QD)
145 if (queue_read(ring, this_size, offset))
153 if (had_reads != reads) {
154 ret = io_uring_submit(ring);
156 fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret));
162 * Queue is full at this point. Find at least one completion.
166 struct io_data *data;
169 ret = io_uring_wait_completion(ring, &cqe);
172 ret = io_uring_get_completion(ring, &cqe);
174 fprintf(stderr, "io_uring_get_completion: %s\n",
181 data = (struct io_data *) (uintptr_t) cqe->user_data;
183 if (cqe->res == -EAGAIN) {
184 queue_prepped(ring, data);
187 fprintf(stderr, "cqe failed: %s\n",
188 strerror(-cqe->res));
190 } else if ((size_t) cqe->res != data->iov.iov_len) {
191 /* Short read/write, adjust and requeue */
192 data->iov.iov_base += cqe->res;
193 data->iov.iov_len -= cqe->res;
194 data->offset += cqe->res;
195 queue_prepped(ring, data);
200 * All done. if write, nothing else to do. if read,
201 * queue up corresponding write.
204 queue_write(ring, data);
205 write_left -= data->first_len;
218 int main(int argc, char *argv[])
220 struct io_uring ring;
225 printf("%s: infile outfile\n", argv[0]);
229 infd = open(argv[1], O_RDONLY);
231 perror("open infile");
234 outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
236 perror("open outfile");
240 if (setup_context(QD, &ring))
242 if (get_file_size(infd, &insize))
245 ret = copy_file(&ring, insize);
249 io_uring_queue_exit(&ring);