Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Simple test program that demonstrates a file copy through io_uring. This
4 * uses the API exposed by liburing.
5 *
6 * Copyright (C) 2018-2019 Jens Axboe
7 */
8#include <stdio.h>
9#include <fcntl.h>
10#include <string.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <assert.h>
14#include <errno.h>
15#include <inttypes.h>
16#include <sys/stat.h>
17#include <sys/ioctl.h>
18
19#include "liburing.h"
20
21#define QD 64
22#define BS (32*1024)
23
24static int infd, outfd;
25
26struct io_data {
27 int read;
28 off_t first_offset, offset;
29 size_t first_len;
30 struct iovec iov;
31};
32
33static int setup_context(unsigned entries, struct io_uring *ring)
34{
35 int ret;
36
37 ret = io_uring_queue_init(entries, ring, 0);
38 if (ret < 0) {
39 fprintf(stderr, "queue_init: %s\n", strerror(-ret));
40 return -1;
41 }
42
43 return 0;
44}
45
46static int get_file_size(int fd, off_t *size)
47{
48 struct stat st;
49
50 if (fstat(fd, &st) < 0)
51 return -1;
52 if (S_ISREG(st.st_mode)) {
53 *size = st.st_size;
54 return 0;
55 } else if (S_ISBLK(st.st_mode)) {
56 unsigned long long bytes;
57
58 if (ioctl(fd, BLKGETSIZE64, &bytes) != 0)
59 return -1;
60
61 *size = bytes;
62 return 0;
63 }
64
65 return -1;
66}
67
68static void queue_prepped(struct io_uring *ring, struct io_data *data)
69{
70 struct io_uring_sqe *sqe;
71
72 sqe = io_uring_get_sqe(ring);
73 assert(sqe);
74
75 if (data->read)
76 io_uring_prep_readv(sqe, infd, &data->iov, 1, data->offset);
77 else
78 io_uring_prep_writev(sqe, outfd, &data->iov, 1, data->offset);
79
80 io_uring_sqe_set_data(sqe, data);
81}
82
83static int queue_read(struct io_uring *ring, off_t size, off_t offset)
84{
85 struct io_uring_sqe *sqe;
86 struct io_data *data;
87
88 sqe = io_uring_get_sqe(ring);
89 if (!sqe)
90 return 1;
91
92 data = malloc(size + sizeof(*data));
93 data->read = 1;
94 data->offset = data->first_offset = offset;
95
96 data->iov.iov_base = data + 1;
97 data->iov.iov_len = size;
98 data->first_len = size;
99
100 io_uring_prep_readv(sqe, infd, &data->iov, 1, offset);
101 io_uring_sqe_set_data(sqe, data);
102 return 0;
103}
104
105static void queue_write(struct io_uring *ring, struct io_data *data)
106{
107 data->read = 0;
108 data->offset = data->first_offset;
109
110 data->iov.iov_base = data + 1;
111 data->iov.iov_len = data->first_len;
112
113 queue_prepped(ring, data);
114 io_uring_submit(ring);
115}
116
117static int copy_file(struct io_uring *ring, off_t insize)
118{
119 unsigned long reads, writes;
120 struct io_uring_cqe *cqe;
121 off_t write_left, offset;
122 int ret;
123
124 write_left = insize;
125 writes = reads = offset = 0;
126
127 while (insize || write_left) {
128 unsigned long had_reads;
129 int got_comp;
130
131 /*
132 * Queue up as many reads as we can
133 */
134 had_reads = reads;
135 while (insize) {
136 off_t this_size = insize;
137
138 if (reads + writes >= QD)
139 break;
140 if (this_size > BS)
141 this_size = BS;
142 else if (!this_size)
143 break;
144
145 if (queue_read(ring, this_size, offset))
146 break;
147
148 insize -= this_size;
149 offset += this_size;
150 reads++;
151 }
152
153 if (had_reads != reads) {
154 ret = io_uring_submit(ring);
155 if (ret < 0) {
156 fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret));
157 break;
158 }
159 }
160
161 /*
162 * Queue is full at this point. Find at least one completion.
163 */
164 got_comp = 0;
165 while (write_left) {
166 struct io_data *data;
167
168 if (!got_comp) {
169 ret = io_uring_wait_completion(ring, &cqe);
170 got_comp = 1;
171 } else
172 ret = io_uring_get_completion(ring, &cqe);
173 if (ret < 0) {
174 fprintf(stderr, "io_uring_get_completion: %s\n",
175 strerror(-ret));
176 return 1;
177 }
178 if (!cqe)
179 break;
180
181 data = (struct io_data *) (uintptr_t) cqe->user_data;
182 if (cqe->res < 0) {
183 if (cqe->res == -EAGAIN) {
184 queue_prepped(ring, data);
185 continue;
186 }
187 fprintf(stderr, "cqe failed: %s\n",
188 strerror(-cqe->res));
189 return 1;
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);
196 continue;
197 }
198
199 /*
200 * All done. if write, nothing else to do. if read,
201 * queue up corresponding write.
202 */
203 if (data->read) {
204 queue_write(ring, data);
205 write_left -= data->first_len;
206 reads--;
207 writes++;
208 } else {
209 free(data);
210 writes--;
211 }
212 }
213 }
214
215 return 0;
216}
217
218int main(int argc, char *argv[])
219{
220 struct io_uring ring;
221 off_t insize;
222 int ret;
223
224 if (argc < 3) {
225 printf("%s: infile outfile\n", argv[0]);
226 return 1;
227 }
228
229 infd = open(argv[1], O_RDONLY);
230 if (infd < 0) {
231 perror("open infile");
232 return 1;
233 }
234 outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
235 if (outfd < 0) {
236 perror("open outfile");
237 return 1;
238 }
239
240 if (setup_context(QD, &ring))
241 return 1;
242 if (get_file_size(infd, &insize))
243 return 1;
244
245 ret = copy_file(&ring, insize);
246
247 close(infd);
248 close(outfd);
249 io_uring_queue_exit(&ring);
250 return ret;
251}