1 /* SPDX-License-Identifier: MIT */
2 #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
11 #include "helpers.h"
12 #include "liburing.h"
13
14 #define RING_SIZE 128
15 enum {
16 OP_NOP,
17 OP_REMOVE_BUFFERS
18 };
19
20 struct test_context {
21 struct io_uring *ring;
22 struct io_uring_sqe **sqes;
23 struct io_uring_cqe *cqes;
24 int nr;
25 };
26
free_context(struct test_context * ctx)27 static void free_context(struct test_context *ctx)
28 {
29 free(ctx->sqes);
30 free(ctx->cqes);
31 memset(ctx, 0, sizeof(*ctx));
32 }
33
init_context(struct test_context * ctx,struct io_uring * ring,int nr,int op)34 static int init_context(struct test_context *ctx, struct io_uring *ring, int nr,
35 int op)
36 {
37 struct io_uring_sqe *sqe;
38 int i;
39
40 memset(ctx, 0, sizeof(*ctx));
41 ctx->nr = nr;
42 ctx->ring = ring;
43 ctx->sqes = t_malloc(nr * sizeof(*ctx->sqes));
44 ctx->cqes = t_malloc(nr * sizeof(*ctx->cqes));
45
46 if (!ctx->sqes || !ctx->cqes)
47 goto err;
48
49 for (i = 0; i < nr; i++) {
50 sqe = io_uring_get_sqe(ring);
51 if (!sqe)
52 goto err;
53 switch (op) {
54 case OP_NOP:
55 io_uring_prep_nop(sqe);
56 break;
57 case OP_REMOVE_BUFFERS:
58 io_uring_prep_remove_buffers(sqe, 10, 1);
59 break;
60 };
61 sqe->user_data = i;
62 ctx->sqes[i] = sqe;
63 }
64
65 return 0;
66 err:
67 free_context(ctx);
68 printf("init context failed\n");
69 return 1;
70 }
71
wait_cqes(struct test_context * ctx)72 static int wait_cqes(struct test_context *ctx)
73 {
74 int ret, i;
75 struct io_uring_cqe *cqe;
76
77 for (i = 0; i < ctx->nr; i++) {
78 ret = io_uring_wait_cqe(ctx->ring, &cqe);
79
80 if (ret < 0) {
81 printf("wait_cqes: wait completion %d\n", ret);
82 return 1;
83 }
84 memcpy(&ctx->cqes[i], cqe, sizeof(*cqe));
85 io_uring_cqe_seen(ctx->ring, cqe);
86 }
87
88 return 0;
89 }
90
test_cancelled_userdata(struct io_uring * ring)91 static int test_cancelled_userdata(struct io_uring *ring)
92 {
93 struct test_context ctx;
94 int ret, i, nr = 100;
95
96 if (init_context(&ctx, ring, nr, OP_NOP))
97 return 1;
98
99 for (i = 0; i < nr; i++)
100 ctx.sqes[i]->flags |= IOSQE_IO_LINK;
101
102 ret = io_uring_submit(ring);
103 if (ret <= 0) {
104 printf("sqe submit failed: %d\n", ret);
105 goto err;
106 }
107
108 if (wait_cqes(&ctx))
109 goto err;
110
111 for (i = 0; i < nr; i++) {
112 if (i != ctx.cqes[i].user_data) {
113 printf("invalid user data\n");
114 goto err;
115 }
116 }
117
118 free_context(&ctx);
119 return 0;
120 err:
121 free_context(&ctx);
122 return 1;
123 }
124
test_thread_link_cancel(struct io_uring * ring)125 static int test_thread_link_cancel(struct io_uring *ring)
126 {
127 struct test_context ctx;
128 int ret, i, nr = 100;
129
130 if (init_context(&ctx, ring, nr, OP_REMOVE_BUFFERS))
131 return 1;
132
133 for (i = 0; i < nr; i++)
134 ctx.sqes[i]->flags |= IOSQE_IO_LINK;
135
136 ret = io_uring_submit(ring);
137 if (ret <= 0) {
138 printf("sqe submit failed: %d\n", ret);
139 goto err;
140 }
141
142 if (wait_cqes(&ctx))
143 goto err;
144
145 for (i = 0; i < nr; i++) {
146 bool fail = false;
147
148 if (i == 0)
149 fail = (ctx.cqes[i].res != -ENOENT);
150 else
151 fail = (ctx.cqes[i].res != -ECANCELED);
152
153 if (fail) {
154 printf("invalid status %d\n", ctx.cqes[i].res);
155 goto err;
156 }
157 }
158
159 free_context(&ctx);
160 return 0;
161 err:
162 free_context(&ctx);
163 return 1;
164 }
165
test_drain_with_linked_timeout(struct io_uring * ring)166 static int test_drain_with_linked_timeout(struct io_uring *ring)
167 {
168 const int nr = 3;
169 struct __kernel_timespec ts = { .tv_sec = 1, .tv_nsec = 0, };
170 struct test_context ctx;
171 int ret, i;
172
173 if (init_context(&ctx, ring, nr * 2, OP_NOP))
174 return 1;
175
176 for (i = 0; i < nr; i++) {
177 io_uring_prep_timeout(ctx.sqes[2 * i], &ts, 0, 0);
178 ctx.sqes[2 * i]->flags |= IOSQE_IO_LINK | IOSQE_IO_DRAIN;
179 io_uring_prep_link_timeout(ctx.sqes[2 * i + 1], &ts, 0);
180 }
181
182 ret = io_uring_submit(ring);
183 if (ret <= 0) {
184 printf("sqe submit failed: %d\n", ret);
185 goto err;
186 }
187
188 if (wait_cqes(&ctx))
189 goto err;
190
191 free_context(&ctx);
192 return 0;
193 err:
194 free_context(&ctx);
195 return 1;
196 }
197
run_drained(struct io_uring * ring,int nr)198 static int run_drained(struct io_uring *ring, int nr)
199 {
200 struct test_context ctx;
201 int ret, i;
202
203 if (init_context(&ctx, ring, nr, OP_NOP))
204 return 1;
205
206 for (i = 0; i < nr; i++)
207 ctx.sqes[i]->flags |= IOSQE_IO_DRAIN;
208
209 ret = io_uring_submit(ring);
210 if (ret <= 0) {
211 printf("sqe submit failed: %d\n", ret);
212 goto err;
213 }
214
215 if (wait_cqes(&ctx))
216 goto err;
217
218 free_context(&ctx);
219 return 0;
220 err:
221 free_context(&ctx);
222 return 1;
223 }
224
test_overflow_hung(struct io_uring * ring)225 static int test_overflow_hung(struct io_uring *ring)
226 {
227 struct io_uring_sqe *sqe;
228 int ret, nr = 10;
229
230 while (*ring->cq.koverflow != 1000) {
231 sqe = io_uring_get_sqe(ring);
232 if (!sqe) {
233 printf("get sqe failed\n");
234 return 1;
235 }
236
237 io_uring_prep_nop(sqe);
238 ret = io_uring_submit(ring);
239 if (ret <= 0) {
240 printf("sqe submit failed: %d\n", ret);
241 return 1;
242 }
243 }
244
245 return run_drained(ring, nr);
246 }
247
test_dropped_hung(struct io_uring * ring)248 static int test_dropped_hung(struct io_uring *ring)
249 {
250 int nr = 10;
251
252 *ring->sq.kdropped = 1000;
253 return run_drained(ring, nr);
254 }
255
main(int argc,char * argv[])256 int main(int argc, char *argv[])
257 {
258 struct io_uring ring, poll_ring, sqthread_ring;
259 struct io_uring_params p;
260 int ret;
261
262 if (argc > 1)
263 return 0;
264
265 memset(&p, 0, sizeof(p));
266 ret = io_uring_queue_init_params(RING_SIZE, &ring, &p);
267 if (ret) {
268 printf("ring setup failed %i\n", ret);
269 return 1;
270 }
271
272 ret = io_uring_queue_init(RING_SIZE, &poll_ring, IORING_SETUP_IOPOLL);
273 if (ret) {
274 printf("poll_ring setup failed\n");
275 return 1;
276 }
277
278
279 ret = test_cancelled_userdata(&poll_ring);
280 if (ret) {
281 printf("test_cancelled_userdata failed\n");
282 return ret;
283 }
284
285 if (!(p.features & IORING_FEAT_NODROP)) {
286 ret = test_overflow_hung(&ring);
287 if (ret) {
288 printf("test_overflow_hung failed\n");
289 return ret;
290 }
291 }
292
293 ret = test_dropped_hung(&ring);
294 if (ret) {
295 printf("test_dropped_hung failed\n");
296 return ret;
297 }
298
299 ret = test_drain_with_linked_timeout(&ring);
300 if (ret) {
301 printf("test_drain_with_linked_timeout failed\n");
302 return ret;
303 }
304
305 ret = t_create_ring(RING_SIZE, &sqthread_ring,
306 IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL);
307 if (ret == T_SETUP_SKIP)
308 return 0;
309 else if (ret < 0)
310 return 1;
311
312 ret = test_thread_link_cancel(&sqthread_ring);
313 if (ret) {
314 printf("test_thread_link_cancel failed\n");
315 return ret;
316 }
317
318 return 0;
319 }
320