• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <sys/resource.h>
11 #include <unistd.h>
12 
13 #include "liburing.h"
14 
15 #define DIE(...)				\
16 	do {					\
17 		fprintf(stderr, __VA_ARGS__);	\
18 		abort();			\
19 	} while(0)
20 
21 static const int RSIZE = 2;
22 static const int OPEN_FLAGS = O_RDWR | O_CREAT | O_LARGEFILE;
23 static const mode_t OPEN_MODE = S_IRUSR | S_IWUSR;
24 
open_io_uring(struct io_uring * ring,int dfd,const char * fn)25 static int open_io_uring(struct io_uring *ring, int dfd, const char *fn)
26 {
27 	struct io_uring_sqe *sqe;
28 	struct io_uring_cqe *cqe;
29 	int ret, fd;
30 
31 	sqe = io_uring_get_sqe(ring);
32 	if (!sqe) {
33 		fprintf(stderr, "failed to get sqe\n");
34 		return 1;
35 	}
36 	io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
37 
38 	ret = io_uring_submit(ring);
39 	if (ret < 0) {
40 		fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
41 		return 1;
42 	}
43 
44 	ret = io_uring_wait_cqe(ring, &cqe);
45 	fd = cqe->res;
46 	io_uring_cqe_seen(ring, cqe);
47 	if (ret < 0) {
48 		fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
49 		return 1;
50 	} else if (fd < 0) {
51 		fprintf(stderr, "io_uring openat failed: %s\n", strerror(-fd));
52 		return 1;
53 	}
54 
55 	close(fd);
56 	return 0;
57 }
58 
prepare_file(int dfd,const char * fn)59 static int prepare_file(int dfd, const char* fn)
60 {
61 	const char buf[] = "foo";
62 	int fd, res;
63 
64 	fd = openat(dfd, fn, OPEN_FLAGS, OPEN_MODE);
65 	if (fd < 0) {
66 		fprintf(stderr, "prepare/open: %s\n", strerror(errno));
67 		return -1;
68 	}
69 
70 	res = pwrite(fd, buf, sizeof(buf), 1ull << 32);
71 	if (res < 0)
72 		fprintf(stderr, "prepare/pwrite: %s\n", strerror(errno));
73 
74 	close(fd);
75 	return res < 0 ? res : 0;
76 }
77 
test_linked_files(int dfd,const char * fn,bool async)78 static int test_linked_files(int dfd, const char *fn, bool async)
79 {
80 	struct io_uring ring;
81 	struct io_uring_sqe *sqe;
82 	char buffer[128];
83 	struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer), };
84 	int ret, fd;
85 	int fds[2];
86 
87 	ret = io_uring_queue_init(10, &ring, 0);
88 	if (ret < 0)
89 		DIE("failed to init io_uring: %s\n", strerror(-ret));
90 
91 	if (pipe(fds)) {
92 		perror("pipe");
93 		return 1;
94 	}
95 
96 	sqe = io_uring_get_sqe(&ring);
97 	if (!sqe) {
98 		printf("get sqe failed\n");
99 		return -1;
100 	}
101 	io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
102 	sqe->flags |= IOSQE_IO_LINK;
103 	if (async)
104 		sqe->flags |= IOSQE_ASYNC;
105 
106 	sqe = io_uring_get_sqe(&ring);
107 	if (!sqe) {
108 		fprintf(stderr, "failed to get sqe\n");
109 		return 1;
110 	}
111 	io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
112 
113 	ret = io_uring_submit(&ring);
114 	if (ret != 2) {
115 		fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
116 		return 1;
117 	}
118 
119 	fd = dup(ring.ring_fd);
120 	if (fd < 0) {
121 		fprintf(stderr, "dup() failed: %s\n", strerror(-fd));
122 		return 1;
123 	}
124 
125 	/* io_uring->flush() */
126 	close(fd);
127 
128 	io_uring_queue_exit(&ring);
129 	return 0;
130 }
131 
test_drained_files(int dfd,const char * fn,bool linked,bool prepend)132 static int test_drained_files(int dfd, const char *fn, bool linked, bool prepend)
133 {
134 	struct io_uring ring;
135 	struct io_uring_sqe *sqe;
136 	char buffer[128];
137 	struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer), };
138 	int ret, fd, fds[2], to_cancel = 0;
139 
140 	ret = io_uring_queue_init(10, &ring, 0);
141 	if (ret < 0)
142 		DIE("failed to init io_uring: %s\n", strerror(-ret));
143 
144 	if (pipe(fds)) {
145 		perror("pipe");
146 		return 1;
147 	}
148 
149 	sqe = io_uring_get_sqe(&ring);
150 	if (!sqe) {
151 		printf("get sqe failed\n");
152 		return -1;
153 	}
154 	io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
155 	sqe->user_data = 0;
156 
157 	if (prepend) {
158 		sqe = io_uring_get_sqe(&ring);
159 		if (!sqe) {
160 			fprintf(stderr, "failed to get sqe\n");
161 			return 1;
162 		}
163 		io_uring_prep_nop(sqe);
164 		sqe->flags |= IOSQE_IO_DRAIN;
165 		to_cancel++;
166 		sqe->user_data = to_cancel;
167 	}
168 
169 	if (linked) {
170 		sqe = io_uring_get_sqe(&ring);
171 		if (!sqe) {
172 			fprintf(stderr, "failed to get sqe\n");
173 			return 1;
174 		}
175 		io_uring_prep_nop(sqe);
176 		sqe->flags |= IOSQE_IO_DRAIN | IOSQE_IO_LINK;
177 		to_cancel++;
178 		sqe->user_data = to_cancel;
179 	}
180 
181 	sqe = io_uring_get_sqe(&ring);
182 	if (!sqe) {
183 		fprintf(stderr, "failed to get sqe\n");
184 		return 1;
185 	}
186 	io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
187 	sqe->flags |= IOSQE_IO_DRAIN;
188 	to_cancel++;
189 	sqe->user_data = to_cancel;
190 
191 
192 	ret = io_uring_submit(&ring);
193 	if (ret != 1 + to_cancel) {
194 		fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
195 		return 1;
196 	}
197 
198 	fd = dup(ring.ring_fd);
199 	if (fd < 0) {
200 		fprintf(stderr, "dup() failed: %s\n", strerror(-fd));
201 		return 1;
202 	}
203 
204 	/*
205 	 * close(), which triggers ->flush(), and io_uring_queue_exit()
206 	 * should successfully return and not hang.
207 	 */
208 	close(fd);
209 	io_uring_queue_exit(&ring);
210 	return 0;
211 }
212 
main(int argc,char * argv[])213 int main(int argc, char *argv[])
214 {
215 	const char *fn = "io_uring_openat_test";
216 	struct io_uring ring;
217 	int ret, dfd;
218 
219 	if (argc > 1)
220 		return 0;
221 
222 	dfd = open("/tmp", O_PATH);
223 	if (dfd < 0)
224 		DIE("open /tmp: %s\n", strerror(errno));
225 
226 	ret = io_uring_queue_init(RSIZE, &ring, 0);
227 	if (ret < 0)
228 		DIE("failed to init io_uring: %s\n", strerror(-ret));
229 
230 	if (prepare_file(dfd, fn))
231 		return 1;
232 
233 	ret = open_io_uring(&ring, dfd, fn);
234 	if (ret) {
235 		fprintf(stderr, "open_io_uring() failed\n");
236 		goto out;
237 	}
238 
239 	ret = test_linked_files(dfd, fn, false);
240 	if (ret) {
241 		fprintf(stderr, "test_linked_files() !async failed\n");
242 		goto out;
243 	}
244 
245 	ret = test_linked_files(dfd, fn, true);
246 	if (ret) {
247 		fprintf(stderr, "test_linked_files() async failed\n");
248 		goto out;
249 	}
250 
251 	ret = test_drained_files(dfd, fn, false, false);
252 	if (ret) {
253 		fprintf(stderr, "test_drained_files() failed\n");
254 		goto out;
255 	}
256 
257 	ret = test_drained_files(dfd, fn, false, true);
258 	if (ret) {
259 		fprintf(stderr, "test_drained_files() middle failed\n");
260 		goto out;
261 	}
262 
263 	ret = test_drained_files(dfd, fn, true, false);
264 	if (ret) {
265 		fprintf(stderr, "test_drained_files() linked failed\n");
266 		goto out;
267 	}
268 out:
269 	io_uring_queue_exit(&ring);
270 	close(dfd);
271 	unlink("/tmp/io_uring_openat_test");
272 	return ret;
273 }
274