blob: 4151ff996dacfdb1b3a1310f6a01c93368d750b9 [file] [log] [blame]
Jens Axboee5024352020-02-11 20:34:12 -07001/* SPDX-License-Identifier: MIT */
Pavel Begunkov6f6de472019-10-25 12:48:30 +03002#include <errno.h>
3#include <stdio.h>
4#include <unistd.h>
5#include <stdlib.h>
6#include <string.h>
7#include <fcntl.h>
8#include <sys/uio.h>
9#include <stdbool.h>
10
Zhiqiang Liu97499812021-02-21 22:58:12 +080011#include "helpers.h"
Pavel Begunkov6f6de472019-10-25 12:48:30 +030012#include "liburing.h"
13
14struct test_context {
15 struct io_uring *ring;
16 struct io_uring_sqe **sqes;
17 struct io_uring_cqe *cqes;
18 int nr;
19};
20
21static void free_context(struct test_context *ctx)
22{
23 free(ctx->sqes);
24 free(ctx->cqes);
25 memset(ctx, 0, sizeof(*ctx));
26}
27
28static int init_context(struct test_context *ctx, struct io_uring *ring, int nr)
29{
30 struct io_uring_sqe *sqe;
31 int i;
32
33 memset(ctx, 0, sizeof(*ctx));
34 ctx->nr = nr;
35 ctx->ring = ring;
Zhiqiang Liu97499812021-02-21 22:58:12 +080036 ctx->sqes = io_uring_malloc(nr * sizeof(*ctx->sqes));
37 ctx->cqes = io_uring_malloc(nr * sizeof(*ctx->cqes));
Pavel Begunkov6f6de472019-10-25 12:48:30 +030038
39 if (!ctx->sqes || !ctx->cqes)
40 goto err;
41
42 for (i = 0; i < nr; i++) {
43 sqe = io_uring_get_sqe(ring);
44 if (!sqe)
45 goto err;
46 io_uring_prep_nop(sqe);
47 sqe->user_data = i;
48 ctx->sqes[i] = sqe;
49 }
50
51 return 0;
52err:
53 free_context(ctx);
54 printf("init context failed\n");
55 return 1;
56}
57
58static int wait_cqes(struct test_context *ctx)
59{
60 int ret, i;
61 struct io_uring_cqe *cqe;
62
63 for (i = 0; i < ctx->nr; i++) {
64 ret = io_uring_wait_cqe(ctx->ring, &cqe);
65
66 if (ret < 0) {
67 printf("wait_cqes: wait completion %d\n", ret);
68 return 1;
69 }
70 memcpy(&ctx->cqes[i], cqe, sizeof(*cqe));
71 io_uring_cqe_seen(ctx->ring, cqe);
72 }
73
74 return 0;
75}
76
77static int test_cancelled_userdata(struct io_uring *ring)
78{
79 struct test_context ctx;
80 int ret, i, nr = 100;
81
82 if (init_context(&ctx, ring, nr))
83 return 1;
84
85 for (i = 0; i < nr; i++)
86 ctx.sqes[i]->flags |= IOSQE_IO_LINK;
87
88 ret = io_uring_submit(ring);
89 if (ret <= 0) {
90 printf("sqe submit failed: %d\n", ret);
91 goto err;
92 }
93
94 if (wait_cqes(&ctx))
95 goto err;
96
Joseph Qia8b3b782019-10-30 15:16:30 +080097 for (i = 0; i < nr; i++) {
Pavel Begunkov6f6de472019-10-25 12:48:30 +030098 if (i != ctx.cqes[i].user_data) {
99 printf("invalid user data\n");
100 goto err;
101 }
102 }
103
104 free_context(&ctx);
105 return 0;
106err:
107 free_context(&ctx);
108 return 1;
109}
110
111static int test_thread_link_cancel(struct io_uring *ring)
112{
113 struct test_context ctx;
114 int ret, i, nr = 100;
115
116 if (init_context(&ctx, ring, nr))
117 return 1;
118
119 for (i = 0; i < nr; i++)
120 ctx.sqes[i]->flags |= IOSQE_IO_LINK;
121
122 ret = io_uring_submit(ring);
123 if (ret <= 0) {
124 printf("sqe submit failed: %d\n", ret);
125 goto err;
126 }
127
128 if (wait_cqes(&ctx))
129 goto err;
130
Joseph Qia8b3b782019-10-30 15:16:30 +0800131 for (i = 0; i < nr; i++) {
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300132 bool fail = false;
133
134 if (i == 0)
135 fail = (ctx.cqes[i].res != -EINVAL);
136 else
137 fail = (ctx.cqes[i].res != -ECANCELED);
138
139 if (fail) {
140 printf("invalid status\n");
141 goto err;
142 }
143 }
144
145 free_context(&ctx);
146 return 0;
147err:
148 free_context(&ctx);
149 return 1;
150}
151
Pavel Begunkova986f462021-01-27 01:41:11 +0000152static int test_drain_with_linked_timeout(struct io_uring *ring)
153{
154 const int nr = 3;
155 struct __kernel_timespec ts = { .tv_sec = 1, .tv_nsec = 0, };
156 struct test_context ctx;
157 int ret, i;
158
159 if (init_context(&ctx, ring, nr * 2))
160 return 1;
161
162 for (i = 0; i < nr; i++) {
163 io_uring_prep_timeout(ctx.sqes[2 * i], &ts, 0, 0);
164 ctx.sqes[2 * i]->flags |= IOSQE_IO_LINK | IOSQE_IO_DRAIN;
165 io_uring_prep_link_timeout(ctx.sqes[2 * i + 1], &ts, 0);
166 }
167
168 ret = io_uring_submit(ring);
169 if (ret <= 0) {
170 printf("sqe submit failed: %d\n", ret);
171 goto err;
172 }
173
174 if (wait_cqes(&ctx))
175 goto err;
176
177 free_context(&ctx);
178 return 0;
179err:
180 free_context(&ctx);
181 return 1;
182}
183
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300184static int run_drained(struct io_uring *ring, int nr)
185{
186 struct test_context ctx;
187 int ret, i;
188
189 if (init_context(&ctx, ring, nr))
190 return 1;
191
192 for (i = 0; i < nr; i++)
193 ctx.sqes[i]->flags |= IOSQE_IO_DRAIN;
194
195 ret = io_uring_submit(ring);
196 if (ret <= 0) {
197 printf("sqe submit failed: %d\n", ret);
198 goto err;
199 }
200
201 if (wait_cqes(&ctx))
202 goto err;
203
204 free_context(&ctx);
205 return 0;
206err:
207 free_context(&ctx);
208 return 1;
209}
210
211static int test_overflow_hung(struct io_uring *ring)
212{
213 struct io_uring_sqe *sqe;
214 int ret, nr = 10;
215
Jens Axboef9363392019-11-09 18:38:14 -0700216 while (*ring->cq.koverflow != 1000) {
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300217 sqe = io_uring_get_sqe(ring);
218 if (!sqe) {
219 printf("get sqe failed\n");
220 return 1;
221 }
222
223 io_uring_prep_nop(sqe);
224 ret = io_uring_submit(ring);
225 if (ret <= 0) {
226 printf("sqe submit failed: %d\n", ret);
227 return 1;
228 }
229 }
230
231 return run_drained(ring, nr);
232}
233
234static int test_dropped_hung(struct io_uring *ring)
235{
236 int nr = 10;
237
238 *ring->sq.kdropped = 1000;
239 return run_drained(ring, nr);
240}
241
242int main(int argc, char *argv[])
243{
244 struct io_uring ring, poll_ring, sqthread_ring;
Jens Axboef9363392019-11-09 18:38:14 -0700245 struct io_uring_params p;
Jens Axboe9fca8692019-11-10 09:48:47 -0700246 int ret, no_sqthread = 0;
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300247
Jens Axboea2141fc2020-05-19 17:36:19 -0600248 if (argc > 1)
249 return 0;
250
Jens Axboef9363392019-11-09 18:38:14 -0700251 memset(&p, 0, sizeof(p));
252 ret = io_uring_queue_init_params(1000, &ring, &p);
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300253 if (ret) {
254 printf("ring setup failed\n");
255 return 1;
256 }
257
258 ret = io_uring_queue_init(1000, &poll_ring, IORING_SETUP_IOPOLL);
259 if (ret) {
260 printf("poll_ring setup failed\n");
261 return 1;
262 }
263
264 ret = io_uring_queue_init(1000, &sqthread_ring,
265 IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL);
266 if (ret) {
Jens Axboe9fca8692019-11-10 09:48:47 -0700267 if (geteuid()) {
268 no_sqthread = 1;
269 } else {
270 printf("poll_ring setup failed\n");
271 return 1;
272 }
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300273 }
274
275 ret = test_cancelled_userdata(&poll_ring);
276 if (ret) {
277 printf("test_cancelled_userdata failed\n");
278 return ret;
279 }
280
Jens Axboe9fca8692019-11-10 09:48:47 -0700281 if (no_sqthread) {
282 printf("test_thread_link_cancel: skipped, not root\n");
283 } else {
284 ret = test_thread_link_cancel(&sqthread_ring);
285 if (ret) {
286 printf("test_thread_link_cancel failed\n");
287 return ret;
288 }
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300289 }
290
Jens Axboef9363392019-11-09 18:38:14 -0700291 if (!(p.features & IORING_FEAT_NODROP)) {
292 ret = test_overflow_hung(&ring);
293 if (ret) {
294 printf("test_overflow_hung failed\n");
295 return ret;
296 }
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300297 }
298
299 ret = test_dropped_hung(&ring);
300 if (ret) {
301 printf("test_dropped_hung failed\n");
302 return ret;
303 }
304
Pavel Begunkova986f462021-01-27 01:41:11 +0000305 ret = test_drain_with_linked_timeout(&ring);
306 if (ret) {
307 printf("test_drain_with_linked_timeout failed\n");
308 return ret;
309 }
310
Pavel Begunkov6f6de472019-10-25 12:48:30 +0300311 return 0;
312}