1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Very basic proof-of-concept for doing a copy with linked SQEs. Needs a
4 * bit of error handling and short read love.
5 */
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <assert.h>
12 #include <errno.h>
13 #include <inttypes.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/ioctl.h>
17 #include "liburing.h"
18
19 #define QD 64
20 #define BS (32*1024)
21
22 struct io_data {
23 size_t offset;
24 int index;
25 struct iovec iov;
26 };
27
28 static int infd, outfd;
29 static int inflight;
30
setup_context(unsigned entries,struct io_uring * ring)31 static int setup_context(unsigned entries, struct io_uring *ring)
32 {
33 int ret;
34
35 ret = io_uring_queue_init(entries, ring, 0);
36 if (ret < 0) {
37 fprintf(stderr, "queue_init: %s\n", strerror(-ret));
38 return -1;
39 }
40
41 return 0;
42 }
43
get_file_size(int fd,off_t * size)44 static int get_file_size(int fd, off_t *size)
45 {
46 struct stat st;
47
48 if (fstat(fd, &st) < 0)
49 return -1;
50 if (S_ISREG(st.st_mode)) {
51 *size = st.st_size;
52 return 0;
53 } else if (S_ISBLK(st.st_mode)) {
54 unsigned long long bytes;
55
56 if (ioctl(fd, BLKGETSIZE64, &bytes) != 0)
57 return -1;
58
59 *size = bytes;
60 return 0;
61 }
62
63 return -1;
64 }
65
queue_rw_pair(struct io_uring * ring,off_t size,off_t offset)66 static void queue_rw_pair(struct io_uring *ring, off_t size, off_t offset)
67 {
68 struct io_uring_sqe *sqe;
69 struct io_data *data;
70 void *ptr;
71
72 ptr = malloc(size + sizeof(*data));
73 data = ptr + size;
74 data->index = 0;
75 data->offset = offset;
76 data->iov.iov_base = ptr;
77 data->iov.iov_len = size;
78
79 sqe = io_uring_get_sqe(ring);
80 io_uring_prep_readv(sqe, infd, &data->iov, 1, offset);
81 sqe->flags |= IOSQE_IO_LINK;
82 io_uring_sqe_set_data(sqe, data);
83
84 sqe = io_uring_get_sqe(ring);
85 io_uring_prep_writev(sqe, outfd, &data->iov, 1, offset);
86 io_uring_sqe_set_data(sqe, data);
87 }
88
handle_cqe(struct io_uring * ring,struct io_uring_cqe * cqe)89 static int handle_cqe(struct io_uring *ring, struct io_uring_cqe *cqe)
90 {
91 struct io_data *data = io_uring_cqe_get_data(cqe);
92 int ret = 0;
93
94 data->index++;
95
96 if (cqe->res < 0) {
97 if (cqe->res == -ECANCELED) {
98 queue_rw_pair(ring, data->iov.iov_len, data->offset);
99 inflight += 2;
100 } else {
101 printf("cqe error: %s\n", strerror(-cqe->res));
102 ret = 1;
103 }
104 }
105
106 if (data->index == 2) {
107 void *ptr = (void *) data - data->iov.iov_len;
108
109 free(ptr);
110 }
111 io_uring_cqe_seen(ring, cqe);
112 return ret;
113 }
114
copy_file(struct io_uring * ring,off_t insize)115 static int copy_file(struct io_uring *ring, off_t insize)
116 {
117 struct io_uring_cqe *cqe;
118 off_t this_size;
119 off_t offset;
120
121 offset = 0;
122 while (insize) {
123 int has_inflight = inflight;
124 int depth;
125
126 while (insize && inflight < QD) {
127 this_size = BS;
128 if (this_size > insize)
129 this_size = insize;
130 queue_rw_pair(ring, this_size, offset);
131 offset += this_size;
132 insize -= this_size;
133 inflight += 2;
134 }
135
136 if (has_inflight != inflight)
137 io_uring_submit(ring);
138
139 if (insize)
140 depth = QD;
141 else
142 depth = 1;
143 while (inflight >= depth) {
144 int ret;
145
146 ret = io_uring_wait_cqe(ring, &cqe);
147 if (ret < 0) {
148 printf("wait cqe: %s\n", strerror(-ret));
149 return 1;
150 }
151 if (handle_cqe(ring, cqe))
152 return 1;
153 inflight--;
154 }
155 }
156
157 return 0;
158 }
159
main(int argc,char * argv[])160 int main(int argc, char *argv[])
161 {
162 struct io_uring ring;
163 off_t insize;
164 int ret;
165
166 if (argc < 3) {
167 printf("%s: infile outfile\n", argv[0]);
168 return 1;
169 }
170
171 infd = open(argv[1], O_RDONLY);
172 if (infd < 0) {
173 perror("open infile");
174 return 1;
175 }
176 outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
177 if (outfd < 0) {
178 perror("open outfile");
179 return 1;
180 }
181
182 if (setup_context(QD, &ring))
183 return 1;
184 if (get_file_size(infd, &insize))
185 return 1;
186
187 ret = copy_file(&ring, insize);
188
189 close(infd);
190 close(outfd);
191 io_uring_queue_exit(&ring);
192 return ret;
193 }
194