• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: basic read/write tests with buffered, O_DIRECT, and SQPOLL
4  */
5 #include <errno.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <fcntl.h>
11 #include <sys/types.h>
12 #include <poll.h>
13 #include <sys/eventfd.h>
14 #include <sys/resource.h>
15 
16 #include "helpers.h"
17 #include "liburing.h"
18 
19 #define FILE_SIZE	(256 * 1024)
20 #define BS		8192
21 #define BUFFERS		(FILE_SIZE / BS)
22 
23 static struct iovec *vecs;
24 static int no_read;
25 static int warned;
26 
fdinfo_read(struct io_uring * ring)27 static void fdinfo_read(struct io_uring *ring)
28 {
29 	char fd_name[128];
30 	char *buf;
31 	int fd;
32 
33 	buf = malloc(4096);
34 
35 	sprintf(fd_name, "/proc/self/fdinfo/%d", ring->ring_fd);
36 	fd = open(fd_name, O_RDONLY);
37 	if (fd < 0) {
38 		perror("open");
39 		return;
40 	}
41 
42 	do {
43 		int ret = read(fd, buf, 4096);
44 
45 		if (ret < 0) {
46 			perror("fdinfo read");
47 			break;
48 		} else if (ret == 4096) {
49 			continue;
50 		}
51 		break;
52 	} while (1);
53 
54 	close(fd);
55 	free(buf);
56 }
57 
__test_io(const char * file,struct io_uring * ring,int write,int buffered,int sqthread,int fixed,int nonvec,int buf_select,int seq,int exp_len)58 static int __test_io(const char *file, struct io_uring *ring, int write,
59 		     int buffered, int sqthread, int fixed, int nonvec,
60 		     int buf_select, int seq, int exp_len)
61 {
62 	struct io_uring_sqe *sqe;
63 	struct io_uring_cqe *cqe;
64 	int open_flags;
65 	int i, fd = -1, ret;
66 	off_t offset;
67 
68 #ifdef VERBOSE
69 	fprintf(stdout, "%s: start %d/%d/%d/%d/%d: ", __FUNCTION__, write,
70 							buffered, sqthread,
71 							fixed, nonvec);
72 #endif
73 	if (write)
74 		open_flags = O_WRONLY;
75 	else
76 		open_flags = O_RDONLY;
77 	if (!buffered)
78 		open_flags |= O_DIRECT;
79 
80 	if (fixed) {
81 		ret = t_register_buffers(ring, vecs, BUFFERS);
82 		if (ret == T_SETUP_SKIP)
83 			return T_EXIT_SKIP;
84 		if (ret != T_SETUP_OK) {
85 			fprintf(stderr, "buffer reg failed: %d\n", ret);
86 			goto err;
87 		}
88 	}
89 
90 	fd = open(file, open_flags);
91 	if (fd < 0) {
92 		if (errno == EINVAL)
93 			return 0;
94 		if (errno == EPERM || errno == EACCES)
95 			return T_EXIT_SKIP;
96 		perror("file open");
97 		goto err;
98 	}
99 
100 	if (sqthread) {
101 		ret = io_uring_register_files(ring, &fd, 1);
102 		if (ret) {
103 			fprintf(stderr, "file reg failed: %d\n", ret);
104 			goto err;
105 		}
106 	}
107 
108 	offset = 0;
109 	for (i = 0; i < BUFFERS; i++) {
110 		sqe = io_uring_get_sqe(ring);
111 		if (!sqe) {
112 			fprintf(stderr, "sqe get failed\n");
113 			goto err;
114 		}
115 		if (!seq)
116 			offset = BS * (rand() % BUFFERS);
117 		if (write) {
118 			int do_fixed = fixed;
119 			int use_fd = fd;
120 
121 			if (sqthread)
122 				use_fd = 0;
123 			if (fixed && (i & 1))
124 				do_fixed = 0;
125 			if (do_fixed) {
126 				io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
127 								vecs[i].iov_len,
128 								offset, i);
129 			} else if (nonvec) {
130 				io_uring_prep_write(sqe, use_fd, vecs[i].iov_base,
131 							vecs[i].iov_len, offset);
132 			} else {
133 				io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
134 								offset);
135 			}
136 		} else {
137 			int do_fixed = fixed;
138 			int use_fd = fd;
139 
140 			if (sqthread)
141 				use_fd = 0;
142 			if (fixed && (i & 1))
143 				do_fixed = 0;
144 			if (do_fixed) {
145 				io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
146 								vecs[i].iov_len,
147 								offset, i);
148 			} else if (nonvec) {
149 				io_uring_prep_read(sqe, use_fd, vecs[i].iov_base,
150 							vecs[i].iov_len, offset);
151 			} else {
152 				io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
153 								offset);
154 			}
155 
156 		}
157 		sqe->user_data = i;
158 		if (sqthread)
159 			sqe->flags |= IOSQE_FIXED_FILE;
160 		if (buf_select) {
161 			if (nonvec)
162 				sqe->addr = 0;
163 			sqe->flags |= IOSQE_BUFFER_SELECT;
164 			sqe->buf_group = buf_select;
165 		}
166 		if (seq)
167 			offset += BS;
168 	}
169 
170 	fdinfo_read(ring);
171 
172 	ret = io_uring_submit(ring);
173 	if (ret != BUFFERS) {
174 		fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
175 		goto err;
176 	}
177 
178 	for (i = 0; i < 10; i++) {
179 		fdinfo_read(ring);
180 		usleep(2);
181 	}
182 
183 	for (i = 0; i < BUFFERS; i++) {
184 		ret = io_uring_wait_cqe(ring, &cqe);
185 		if (ret) {
186 			fprintf(stderr, "wait_cqe=%d\n", ret);
187 			goto err;
188 		}
189 		if (cqe->res == -EINVAL && nonvec) {
190 			if (!warned) {
191 				fprintf(stdout, "Non-vectored IO not "
192 					"supported, skipping\n");
193 				warned = 1;
194 				no_read = 1;
195 			}
196 		} else if (exp_len == -1) {
197 			int iov_len = vecs[cqe->user_data].iov_len;
198 
199 			if (cqe->res != iov_len) {
200 				fprintf(stderr, "cqe res %d, wanted %d\n",
201 					cqe->res, iov_len);
202 				goto err;
203 			}
204 		} else if (cqe->res != exp_len) {
205 			fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, exp_len);
206 			goto err;
207 		}
208 		if (buf_select && exp_len == BS) {
209 			int bid = cqe->flags >> 16;
210 			unsigned char *ptr = vecs[bid].iov_base;
211 			int j;
212 
213 			for (j = 0; j < BS; j++) {
214 				if (ptr[j] == cqe->user_data)
215 					continue;
216 
217 				fprintf(stderr, "Data mismatch! bid=%d, "
218 						"wanted=%d, got=%d\n", bid,
219 						(int)cqe->user_data, ptr[j]);
220 				return 1;
221 			}
222 		}
223 		io_uring_cqe_seen(ring, cqe);
224 	}
225 
226 	if (fixed) {
227 		ret = io_uring_unregister_buffers(ring);
228 		if (ret) {
229 			fprintf(stderr, "buffer unreg failed: %d\n", ret);
230 			goto err;
231 		}
232 	}
233 	if (sqthread) {
234 		ret = io_uring_unregister_files(ring);
235 		if (ret) {
236 			fprintf(stderr, "file unreg failed: %d\n", ret);
237 			goto err;
238 		}
239 	}
240 
241 	close(fd);
242 #ifdef VERBOSE
243 	fprintf(stdout, "PASS\n");
244 #endif
245 	return 0;
246 err:
247 #ifdef VERBOSE
248 	fprintf(stderr, "FAILED\n");
249 #endif
250 	if (fd != -1)
251 		close(fd);
252 	return 1;
253 }
test_io(const char * file,int write,int buffered,int sqthread,int fixed,int nonvec,int exp_len)254 static int test_io(const char *file, int write, int buffered, int sqthread,
255 		   int fixed, int nonvec, int exp_len)
256 {
257 	struct io_uring ring;
258 	int ret, ring_flags = 0;
259 
260 	if (sqthread)
261 		ring_flags = IORING_SETUP_SQPOLL;
262 
263 	ret = t_create_ring(64, &ring, ring_flags);
264 	if (ret == T_SETUP_SKIP)
265 		return 0;
266 	if (ret != T_SETUP_OK) {
267 		fprintf(stderr, "ring create failed: %d\n", ret);
268 		return 1;
269 	}
270 
271 	ret = __test_io(file, &ring, write, buffered, sqthread, fixed, nonvec,
272 			0, 0, exp_len);
273 	io_uring_queue_exit(&ring);
274 	return ret;
275 }
276 
has_nonvec_read(void)277 static int has_nonvec_read(void)
278 {
279 	struct io_uring_probe *p;
280 	struct io_uring ring;
281 	int ret;
282 
283 	ret = io_uring_queue_init(1, &ring, 0);
284 	if (ret) {
285 		fprintf(stderr, "queue init failed: %d\n", ret);
286 		exit(ret);
287 	}
288 
289 	p = t_calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
290 	ret = io_uring_register_probe(&ring, p, 256);
291 	/* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */
292 	if (ret == -EINVAL) {
293 out:
294 		io_uring_queue_exit(&ring);
295 		free(p);
296 		return 0;
297 	} else if (ret) {
298 		fprintf(stderr, "register_probe: %d\n", ret);
299 		goto out;
300 	}
301 
302 	if (p->ops_len <= IORING_OP_READ)
303 		goto out;
304 	if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED))
305 		goto out;
306 	io_uring_queue_exit(&ring);
307 	free(p);
308 	return 1;
309 }
310 
test_eventfd_read(int flags)311 static int test_eventfd_read(int flags)
312 {
313 	struct io_uring ring;
314 	int fd, ret;
315 	eventfd_t event;
316 	struct io_uring_sqe *sqe;
317 	struct io_uring_cqe *cqe;
318 
319 	if (no_read)
320 		return 0;
321 	ret = t_create_ring(64, &ring, flags);
322 	if (ret == T_SETUP_SKIP)
323 		return 0;
324 	if (ret != T_SETUP_OK) {
325 		if (ret == -EINVAL)
326 			return 0;
327 		fprintf(stderr, "ring create failed: %d\n", ret);
328 		return 1;
329 	}
330 
331 	fd = eventfd(1, 0);
332 	if (fd < 0) {
333 		perror("eventfd");
334 		return 1;
335 	}
336 	sqe = io_uring_get_sqe(&ring);
337 	io_uring_prep_read(sqe, fd, &event, sizeof(eventfd_t), 0);
338 	ret = io_uring_submit(&ring);
339 	if (ret != 1) {
340 		fprintf(stderr, "submitted %d\n", ret);
341 		return 1;
342 	}
343 	fdinfo_read(&ring);
344 	eventfd_write(fd, 1);
345 	ret = io_uring_wait_cqe(&ring, &cqe);
346 	if (ret) {
347 		fprintf(stderr, "wait_cqe=%d\n", ret);
348 		return 1;
349 	}
350 	if (cqe->res == -EINVAL) {
351 		fprintf(stdout, "eventfd IO not supported, skipping\n");
352 	} else if (cqe->res != sizeof(eventfd_t)) {
353 		fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res,
354 						(int) sizeof(eventfd_t));
355 		return 1;
356 	}
357 	io_uring_cqe_seen(&ring, cqe);
358 	return 0;
359 }
360 
main(int argc,char * argv[])361 int main(int argc, char *argv[])
362 {
363 	int i, ret, nr;
364 	char buf[256];
365 	char *fname;
366 
367 	if (argc > 1) {
368 		fname = argv[1];
369 	} else {
370 		srand((unsigned)time(NULL));
371 		snprintf(buf, sizeof(buf), ".basic-rw-%u-%u",
372 			(unsigned)rand(), (unsigned)getpid());
373 		fname = buf;
374 		t_create_file(fname, FILE_SIZE);
375 	}
376 
377 	signal(SIGXFSZ, SIG_IGN);
378 
379 	vecs = t_create_buffers(BUFFERS, BS);
380 
381 	/* if we don't have nonvec read, skip testing that */
382 	nr = has_nonvec_read() ? 32 : 16;
383 
384 	for (i = 0; i < nr; i++) {
385 		int write = (i & 1) != 0;
386 		int buffered = (i & 2) != 0;
387 		int sqthread = (i & 4) != 0;
388 		int fixed = (i & 8) != 0;
389 		int nonvec = (i & 16) != 0;
390 
391 		ret = test_io(fname, write, buffered, sqthread, fixed, nonvec,
392 			      BS);
393 		if (ret == T_EXIT_SKIP)
394 			continue;
395 		if (ret) {
396 			fprintf(stderr, "test_io failed %d/%d/%d/%d/%d\n",
397 				write, buffered, sqthread, fixed, nonvec);
398 			goto err;
399 		}
400 	}
401 
402 	ret = test_eventfd_read(0);
403 	if (ret) {
404 		fprintf(stderr, "eventfd read 0 failed\n");
405 		goto err;
406 	}
407 
408 	ret = test_eventfd_read(IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
409 	if (ret) {
410 		fprintf(stderr, "eventfd read defer failed\n");
411 		goto err;
412 	}
413 
414 	ret = test_eventfd_read(IORING_SETUP_SQPOLL);
415 	if (ret) {
416 		fprintf(stderr, "eventfd read sqpoll failed\n");
417 		goto err;
418 	}
419 
420 	if (fname != argv[1])
421 		unlink(fname);
422 	return 0;
423 err:
424 	if (fname != argv[1])
425 		unlink(fname);
426 	return 1;
427 }
428