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
16 #include <sys/types.h>
18 #include <sys/ioctl.h>
25 static int infd
, outfd
;
29 off_t first_offset
, offset
;
34 static int setup_context(unsigned entries
, struct io_uring
*ring
)
38 ret
= io_uring_queue_init(entries
, ring
, 0);
40 fprintf(stderr
, "queue_init: %s\n", strerror(-ret
));
47 static int get_file_size(int fd
, off_t
*size
)
51 if (fstat(fd
, &st
) < 0)
53 if (S_ISREG(st
.st_mode
)) {
56 } else if (S_ISBLK(st
.st_mode
)) {
57 unsigned long long bytes
;
59 if (ioctl(fd
, BLKGETSIZE64
, &bytes
) != 0)
69 static void queue_prepped(struct io_uring
*ring
, struct io_data
*data
)
71 struct io_uring_sqe
*sqe
;
73 sqe
= io_uring_get_sqe(ring
);
77 io_uring_prep_readv(sqe
, infd
, &data
->iov
, 1, data
->offset
);
79 io_uring_prep_writev(sqe
, outfd
, &data
->iov
, 1, data
->offset
);
81 io_uring_sqe_set_data(sqe
, data
);
84 static int queue_read(struct io_uring
*ring
, off_t size
, off_t offset
)
86 struct io_uring_sqe
*sqe
;
89 data
= malloc(size
+ sizeof(*data
));
93 sqe
= io_uring_get_sqe(ring
);
100 data
->offset
= data
->first_offset
= offset
;
102 data
->iov
.iov_base
= data
+ 1;
103 data
->iov
.iov_len
= size
;
104 data
->first_len
= size
;
106 io_uring_prep_readv(sqe
, infd
, &data
->iov
, 1, offset
);
107 io_uring_sqe_set_data(sqe
, data
);
111 static void queue_write(struct io_uring
*ring
, struct io_data
*data
)
114 data
->offset
= data
->first_offset
;
116 data
->iov
.iov_base
= data
+ 1;
117 data
->iov
.iov_len
= data
->first_len
;
119 queue_prepped(ring
, data
);
120 io_uring_submit(ring
);
123 static int copy_file(struct io_uring
*ring
, off_t insize
)
125 unsigned long reads
, writes
;
126 struct io_uring_cqe
*cqe
;
127 off_t write_left
, offset
;
131 writes
= reads
= offset
= 0;
133 while (insize
|| write_left
) {
134 unsigned long had_reads
;
138 * Queue up as many reads as we can
142 off_t this_size
= insize
;
144 if (reads
+ writes
>= QD
)
151 if (queue_read(ring
, this_size
, offset
))
159 if (had_reads
!= reads
) {
160 ret
= io_uring_submit(ring
);
162 fprintf(stderr
, "io_uring_submit: %s\n", strerror(-ret
));
168 * Queue is full at this point. Find at least one completion.
172 struct io_data
*data
;
175 ret
= io_uring_wait_cqe(ring
, &cqe
);
178 ret
= io_uring_peek_cqe(ring
, &cqe
);
180 fprintf(stderr
, "io_uring_peek_cqe: %s\n",
187 data
= io_uring_cqe_get_data(cqe
);
189 if (cqe
->res
== -EAGAIN
) {
190 queue_prepped(ring
, data
);
191 io_uring_cqe_seen(ring
, cqe
);
194 fprintf(stderr
, "cqe failed: %s\n",
195 strerror(-cqe
->res
));
197 } else if ((size_t) cqe
->res
!= data
->iov
.iov_len
) {
198 /* Short read/write, adjust and requeue */
199 data
->iov
.iov_base
+= cqe
->res
;
200 data
->iov
.iov_len
-= cqe
->res
;
201 data
->offset
+= cqe
->res
;
202 queue_prepped(ring
, data
);
203 io_uring_cqe_seen(ring
, cqe
);
208 * All done. if write, nothing else to do. if read,
209 * queue up corresponding write.
212 queue_write(ring
, data
);
213 write_left
-= data
->first_len
;
220 io_uring_cqe_seen(ring
, cqe
);
227 int main(int argc
, char *argv
[])
229 struct io_uring ring
;
234 printf("%s: infile outfile\n", argv
[0]);
238 infd
= open(argv
[1], O_RDONLY
);
240 perror("open infile");
243 outfd
= open(argv
[2], O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
245 perror("open outfile");
249 if (setup_context(QD
, &ring
))
251 if (get_file_size(infd
, &insize
))
254 ret
= copy_file(&ring
, insize
);
258 io_uring_queue_exit(&ring
);