blob: 1a5ff27e46ac636bfadb70f35069e04f0c7d2567 [file] [log] [blame]
Jens Axboee5024352020-02-11 20:34:12 -07001/* SPDX-License-Identifier: MIT */
Jeff Moyer765ba232019-03-04 17:35:49 -05002/*
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 <jmoyer@redhat.com>
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>
Stefan Hajnoczic31c7ec2019-07-24 09:24:50 +010026#include "liburing.h"
27#include "liburing/barrier.h"
Jens Axboe96144ea2019-12-01 11:21:39 -070028#include "../src/syscall.h"
Jeff Moyer765ba232019-03-04 17:35:49 -050029
30#define IORING_MAX_ENTRIES 4096
31
32int
33expect_failed_submit(struct io_uring *ring, int error)
34{
35 int ret;
36
37 ret = io_uring_submit(ring);
38 if (ret == 1) {
39 printf("expected failure, but io_uring_submit succeeded.\n");
40 return 1;
41 }
42
43 if (errno != error) {
44 printf("expected %d, got %d\n", error, errno);
45 return 1;
46 }
47
48 return 0;
49}
50
51int
52expect_fail(int fd, unsigned int to_submit, unsigned int min_complete,
53 unsigned int flags, sigset_t *sig, int error)
54{
55 int ret;
56
Jens Axboe96144ea2019-12-01 11:21:39 -070057 ret = __sys_io_uring_enter(fd, to_submit, min_complete, flags, sig);
Jeff Moyer765ba232019-03-04 17:35:49 -050058 if (ret != -1) {
59 printf("expected %s, but call succeeded\n", strerror(error));
60 return 1;
61 }
62
63 if (errno != error) {
64 printf("expected %d, got %d\n", error, errno);
65 return 1;
66 }
67
68 return 0;
69}
70
71int
72try_io_uring_enter(int fd, unsigned int to_submit, unsigned int min_complete,
73 unsigned int flags, sigset_t *sig, int expect, int error)
74{
75 int ret;
76
77 printf("io_uring_enter(%d, %u, %u, %u, %p)\n", fd, to_submit,
78 min_complete, flags, sig);
79
80 if (expect == -1)
81 return expect_fail(fd, to_submit, min_complete,
82 flags, sig, error);
83
Jens Axboe96144ea2019-12-01 11:21:39 -070084 ret = __sys_io_uring_enter(fd, to_submit, min_complete, flags, sig);
Jeff Moyer765ba232019-03-04 17:35:49 -050085 if (ret != expect) {
86 printf("Expected %d, got %d\n", expect, errno);
87 return 1;
88 }
89
90 return 0;
91}
92
93/*
94 * prep a read I/O. index is treated like a block number.
95 */
96int
Jens Axboeca50c1f2019-09-10 17:57:28 -060097setup_file(char *template, off_t len)
Jeff Moyer765ba232019-03-04 17:35:49 -050098{
99 int fd, ret;
Jeff Moyer765ba232019-03-04 17:35:49 -0500100 char buf[4096];
101
102 fd = mkstemp(template);
103 if (fd < 0) {
104 perror("mkstemp");
105 exit(1);
106 }
107 ret = ftruncate(fd, len);
108 if (ret < 0) {
109 perror("ftruncate");
110 exit(1);
111 }
112
113 ret = read(fd, buf, 4096);
114 if (ret != 4096) {
115 printf("read returned %d, expected 4096\n", ret);
116 exit(1);
117 }
118
119 return fd;
120}
121
122void
123io_prep_read(struct io_uring_sqe *sqe, int fd, off_t offset, size_t len)
124{
125 struct iovec *iov;
126
127 iov = malloc(sizeof(*iov));
128 assert(iov);
129
130 iov->iov_base = malloc(len);
131 assert(iov->iov_base);
132 iov->iov_len = len;
133
134 io_uring_prep_readv(sqe, fd, iov, 1, offset);
135 io_uring_sqe_set_data(sqe, iov); // free on completion
136}
137
138void
139reap_events(struct io_uring *ring, unsigned nr)
140{
141 int ret;
142 unsigned left = nr;
143 struct io_uring_cqe *cqe;
144 struct iovec *iov;
145 struct timeval start, now, elapsed;
146
147 printf("Reaping %u I/Os\n", nr);
148 gettimeofday(&start, NULL);
149 while (left) {
Jens Axboe39e0ebd2019-04-18 08:32:06 -0600150 ret = io_uring_wait_cqe(ring, &cqe);
Jeff Moyer765ba232019-03-04 17:35:49 -0500151 if (ret < 0) {
Jens Axboe39e0ebd2019-04-18 08:32:06 -0600152 printf("io_uring_wait_cqe returned %d\n", ret);
Jeff Moyer765ba232019-03-04 17:35:49 -0500153 printf("expected success\n");
154 exit(1);
155 }
156 if (cqe->res != 4096)
157 printf("cqe->res: %d, expected 4096\n", cqe->res);
Jens Axboedb11f112019-04-07 18:36:43 -0600158 iov = io_uring_cqe_get_data(cqe);
Jeff Moyer765ba232019-03-04 17:35:49 -0500159 free(iov->iov_base);
160 free(iov);
161 left--;
Jens Axboe76b61eb2019-04-17 09:25:32 -0600162 io_uring_cqe_seen(ring, cqe);
Jeff Moyer765ba232019-03-04 17:35:49 -0500163
164 gettimeofday(&now, NULL);
165 timersub(&now, &start, &elapsed);
166 if (elapsed.tv_sec > 10) {
167 printf("Timed out waiting for I/Os to complete.\n");
168 printf("%u expected, %u completed\n", nr, left);
169 break;
170 }
171 }
172}
173
174void
175submit_io(struct io_uring *ring, unsigned nr)
176{
177 int fd, ret;
178 off_t file_len;
179 unsigned i;
Jens Axboeca50c1f2019-09-10 17:57:28 -0600180 static char template[32] = "/tmp/io_uring_enter-test.XXXXXX";
Jeff Moyer765ba232019-03-04 17:35:49 -0500181 struct io_uring_sqe *sqe;
182
183 printf("Allocating %u sqes\n", nr);
184 file_len = nr * 4096;
Jens Axboeca50c1f2019-09-10 17:57:28 -0600185 fd = setup_file(template, file_len);
Jeff Moyer765ba232019-03-04 17:35:49 -0500186 for (i = 0; i < nr; i++) {
187 /* allocate an sqe */
188 sqe = io_uring_get_sqe(ring);
189 /* fill it in */
190 io_prep_read(sqe, fd, i * 4096, 4096);
191 }
192
193 /* submit the I/Os */
194 printf("Submitting %u I/Os\n", nr);
195 ret = io_uring_submit(ring);
Jens Axboeca50c1f2019-09-10 17:57:28 -0600196 unlink(template);
Jeff Moyer765ba232019-03-04 17:35:49 -0500197 if (ret < 0) {
198 perror("io_uring_enter");
199 exit(1);
200 }
201 printf("Done\n");
202}
203
204int
205main(int argc, char **argv)
206{
207 int ret;
208 unsigned int status = 0;
209 struct io_uring ring;
210 struct io_uring_sq *sq = &ring.sq;
211 unsigned ktail, mask, index;
212 unsigned sq_entries;
213 unsigned completed, dropped;
214
Jens Axboea2141fc2020-05-19 17:36:19 -0600215 if (argc > 1)
216 return 0;
217
Jeff Moyer765ba232019-03-04 17:35:49 -0500218 ret = io_uring_queue_init(IORING_MAX_ENTRIES, &ring, 0);
219 if (ret < 0) {
220 perror("io_uring_queue_init");
221 exit(1);
222 }
223 mask = *sq->kring_mask;
224
225 /* invalid flags */
226 status |= try_io_uring_enter(ring.ring_fd, 1, 0, ~0U, NULL, -1, EINVAL);
227
228 /* invalid fd, EBADF */
229 status |= try_io_uring_enter(-1, 0, 0, 0, NULL, -1, EBADF);
230
231 /* valid, non-ring fd, EOPNOTSUPP */
232 status |= try_io_uring_enter(0, 0, 0, 0, NULL, -1, EOPNOTSUPP);
233
234 /* to_submit: 0, flags: 0; should get back 0. */
235 status |= try_io_uring_enter(ring.ring_fd, 1, 0, 0, NULL, 0, 0);
236
237 /* fill the sq ring */
238 sq_entries = *ring.sq.kring_entries;
239 submit_io(&ring, sq_entries);
240 printf("Waiting for %u events\n", sq_entries);
Jens Axboe96144ea2019-12-01 11:21:39 -0700241 ret = __sys_io_uring_enter(ring.ring_fd, 0, sq_entries,
242 IORING_ENTER_GETEVENTS, NULL);
Jeff Moyer765ba232019-03-04 17:35:49 -0500243 if (ret < 0) {
244 perror("io_uring_enter");
245 status = 1;
246 } else {
247 /*
248 * This is a non-IOPOLL ring, which means that io_uring_enter
249 * should not return until min_complete events are available
250 * in the completion queue.
251 */
252 completed = *ring.cq.ktail - *ring.cq.khead;
253 if (completed != sq_entries) {
254 printf("Submitted %u I/Os, but only got %u completions\n",
255 sq_entries, completed);
256 status = 1;
257 }
258 reap_events(&ring, sq_entries);
259 }
260
261 /*
262 * Add an invalid index to the submission queue. This should
263 * result in the dropped counter increasing.
264 */
265 printf("Submitting invalid sqe index.\n");
266 index = *sq->kring_entries + 1; // invalid index
267 dropped = *sq->kdropped;
268 ktail = *sq->ktail;
269 sq->array[ktail & mask] = index;
270 ++ktail;
Bart Van Asscheecefd792019-07-01 14:42:32 -0700271 /*
272 * Ensure that the kernel sees the SQE update before it sees the tail
273 * update.
274 */
Julia Suvorova552c6a02019-08-19 08:45:28 -0600275 io_uring_smp_store_release(sq->ktail, ktail);
Jeff Moyer765ba232019-03-04 17:35:49 -0500276
Jens Axboe96144ea2019-12-01 11:21:39 -0700277 ret = __sys_io_uring_enter(ring.ring_fd, 1, 0, 0, NULL);
Jeff Moyer765ba232019-03-04 17:35:49 -0500278 /* now check to see if our sqe was dropped */
279 if (*sq->kdropped == dropped) {
280 printf("dropped counter did not increase\n");
281 status = 1;
282 }
283
284 if (!status) {
285 printf("PASS\n");
286 return 0;
287 }
288
289 printf("FAIL\n");
290 return -1;
291}