xref: /aosp_15_r20/external/liburing/examples/link-cp.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
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