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