xref: /aosp_15_r20/external/liburing/test/io_uring_enter.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * io_uring_enter.c
4  *
5  * Description: Unit tests for the io_uring_enter system call.
6  *
7  * Copyright 2019, Red Hat, Inc.
8  * Author: Jeff Moyer <[email protected]>
9  */
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <sys/sysinfo.h>
17 #include <poll.h>
18 #include <assert.h>
19 #include <sys/uio.h>
20 #include <sys/mman.h>
21 #include <linux/mman.h>
22 #include <sys/time.h>
23 #include <sys/resource.h>
24 #include <limits.h>
25 #include <sys/time.h>
26 
27 #include "helpers.h"
28 #include "liburing.h"
29 #include "liburing/barrier.h"
30 #include "../src/syscall.h"
31 
32 #define IORING_MAX_ENTRIES 4096
33 #define IORING_MAX_ENTRIES_FALLBACK 128
34 
expect_fail(int fd,unsigned int to_submit,unsigned int min_complete,unsigned int flags,sigset_t * sig,int error)35 static int expect_fail(int fd, unsigned int to_submit,
36 		       unsigned int min_complete, unsigned int flags,
37 		       sigset_t *sig, int error)
38 {
39 	int ret;
40 
41 	ret = __sys_io_uring_enter(fd, to_submit, min_complete, flags, sig);
42 	if (ret != -1) {
43 		fprintf(stderr, "expected %s, but call succeeded\n", strerror(error));
44 		return 1;
45 	}
46 
47 	if (errno != error) {
48 		fprintf(stderr, "expected %d, got %d\n", error, errno);
49 		return 1;
50 	}
51 
52 	return 0;
53 }
54 
try_io_uring_enter(int fd,unsigned int to_submit,unsigned int min_complete,unsigned int flags,sigset_t * sig,int expect,int error)55 static int try_io_uring_enter(int fd, unsigned int to_submit,
56 			      unsigned int min_complete, unsigned int flags,
57 			      sigset_t *sig, int expect, int error)
58 {
59 	int ret;
60 
61 	if (expect == -1)
62 		return expect_fail(fd, to_submit, min_complete,
63 				   flags, sig, error);
64 
65 	ret = __sys_io_uring_enter(fd, to_submit, min_complete, flags, sig);
66 	if (ret != expect) {
67 		fprintf(stderr, "Expected %d, got %d\n", expect, errno);
68 		return 1;
69 	}
70 
71 	return 0;
72 }
73 
74 /*
75  * prep a read I/O.  index is treated like a block number.
76  */
setup_file(char * template,off_t len)77 static int setup_file(char *template, off_t len)
78 {
79 	int fd, ret;
80 	char buf[4096];
81 
82 	fd = mkstemp(template);
83 	if (fd < 0) {
84 		perror("mkstemp");
85 		exit(1);
86 	}
87 	ret = ftruncate(fd, len);
88 	if (ret < 0) {
89 		perror("ftruncate");
90 		exit(1);
91 	}
92 
93 	ret = read(fd, buf, 4096);
94 	if (ret != 4096) {
95 		fprintf(stderr, "read returned %d, expected 4096\n", ret);
96 		exit(1);
97 	}
98 
99 	return fd;
100 }
101 
io_prep_read(struct io_uring_sqe * sqe,int fd,off_t offset,size_t len)102 static void io_prep_read(struct io_uring_sqe *sqe, int fd, off_t offset,
103 			 size_t len)
104 {
105 	struct iovec *iov;
106 
107 	iov = t_malloc(sizeof(*iov));
108 	assert(iov);
109 
110 	iov->iov_base = t_malloc(len);
111 	assert(iov->iov_base);
112 	iov->iov_len = len;
113 
114 	io_uring_prep_readv(sqe, fd, iov, 1, offset);
115 	io_uring_sqe_set_data(sqe, iov); // free on completion
116 }
117 
reap_events(struct io_uring * ring,unsigned nr)118 static void reap_events(struct io_uring *ring, unsigned nr)
119 {
120 	int ret;
121 	unsigned left = nr;
122 	struct io_uring_cqe *cqe;
123 	struct iovec *iov;
124 	struct timeval start, now, elapsed;
125 
126 	gettimeofday(&start, NULL);
127 	while (left) {
128 		ret = io_uring_wait_cqe(ring, &cqe);
129 		if (ret < 0) {
130 			fprintf(stderr, "io_uring_wait_cqe returned %d\n", ret);
131 			exit(1);
132 		}
133 		if (cqe->res != 4096)
134 			fprintf(stderr, "cqe->res: %d, expected 4096\n", cqe->res);
135 		iov = io_uring_cqe_get_data(cqe);
136 		free(iov->iov_base);
137 		free(iov);
138 		left--;
139 		io_uring_cqe_seen(ring, cqe);
140 
141 		gettimeofday(&now, NULL);
142 		timersub(&now, &start, &elapsed);
143 		if (elapsed.tv_sec > 10) {
144 			fprintf(stderr, "Timed out waiting for I/Os to complete.\n");
145 			fprintf(stderr, "%u expected, %u completed\n", nr, left);
146 			break;
147 		}
148 	}
149 }
150 
submit_io(struct io_uring * ring,unsigned nr)151 static void submit_io(struct io_uring *ring, unsigned nr)
152 {
153 	int fd, ret;
154 	off_t file_len;
155 	unsigned i;
156 	static char template[32] = "/tmp/io_uring_enter-test.XXXXXX";
157 	struct io_uring_sqe *sqe;
158 
159 	file_len = nr * 4096;
160 	fd = setup_file(template, file_len);
161 	for (i = 0; i < nr; i++) {
162 		/* allocate an sqe */
163 		sqe = io_uring_get_sqe(ring);
164 		/* fill it in */
165 		io_prep_read(sqe, fd, i * 4096, 4096);
166 	}
167 
168 	/* submit the I/Os */
169 	ret = io_uring_submit(ring);
170 	unlink(template);
171 	if (ret < 0) {
172 		perror("io_uring_enter");
173 		exit(1);
174 	}
175 }
176 
main(int argc,char ** argv)177 int main(int argc, char **argv)
178 {
179 	int ret;
180 	unsigned int status = 0;
181 	struct io_uring ring;
182 	struct io_uring_sq *sq = &ring.sq;
183 	unsigned ktail, mask, index;
184 	unsigned sq_entries;
185 	unsigned completed, dropped;
186 
187 	if (argc > 1)
188 		return 0;
189 
190 	ret = io_uring_queue_init(IORING_MAX_ENTRIES, &ring, 0);
191 	if (ret == -ENOMEM)
192 		ret = io_uring_queue_init(IORING_MAX_ENTRIES_FALLBACK, &ring, 0);
193 	if (ret < 0) {
194 		perror("io_uring_queue_init");
195 		exit(1);
196 	}
197 	mask = *sq->kring_mask;
198 
199 	/* invalid flags */
200 	status |= try_io_uring_enter(ring.ring_fd, 1, 0, ~0U, NULL, -1, EINVAL);
201 
202 	/* invalid fd, EBADF */
203 	status |= try_io_uring_enter(-1, 0, 0, 0, NULL, -1, EBADF);
204 
205 	/* valid, non-ring fd, EOPNOTSUPP */
206 	status |= try_io_uring_enter(0, 0, 0, 0, NULL, -1, EOPNOTSUPP);
207 
208 	/* to_submit: 0, flags: 0;  should get back 0. */
209 	status |= try_io_uring_enter(ring.ring_fd, 0, 0, 0, NULL, 0, 0);
210 
211 	/* fill the sq ring */
212 	sq_entries = *ring.sq.kring_entries;
213 	submit_io(&ring, sq_entries);
214 	ret = __sys_io_uring_enter(ring.ring_fd, 0, sq_entries,
215 					IORING_ENTER_GETEVENTS, NULL);
216 	if (ret < 0) {
217 		perror("io_uring_enter");
218 		status = 1;
219 	} else {
220 		/*
221 		 * This is a non-IOPOLL ring, which means that io_uring_enter
222 		 * should not return until min_complete events are available
223 		 * in the completion queue.
224 		 */
225 		completed = *ring.cq.ktail - *ring.cq.khead;
226 		if (completed != sq_entries) {
227 			fprintf(stderr, "Submitted %u I/Os, but only got %u completions\n",
228 			       sq_entries, completed);
229 			status = 1;
230 		}
231 		reap_events(&ring, sq_entries);
232 	}
233 
234 	/*
235 	 * Add an invalid index to the submission queue.  This should
236 	 * result in the dropped counter increasing.
237 	 */
238 	index = *sq->kring_entries + 1; // invalid index
239 	dropped = *sq->kdropped;
240 	ktail = *sq->ktail;
241 	sq->array[ktail & mask] = index;
242 	++ktail;
243 	/*
244 	 * Ensure that the kernel sees the SQE update before it sees the tail
245 	 * update.
246 	 */
247 	io_uring_smp_store_release(sq->ktail, ktail);
248 
249 	ret = __sys_io_uring_enter(ring.ring_fd, 1, 0, 0, NULL);
250 	/* now check to see if our sqe was dropped */
251 	if (*sq->kdropped == dropped) {
252 		fprintf(stderr, "dropped counter did not increase\n");
253 		status = 1;
254 	}
255 
256 	if (!status)
257 		return 0;
258 
259 	fprintf(stderr, "FAIL\n");
260 	return -1;
261 }
262