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 struct test_context {
15 struct io_uring *ring;
16 struct io_uring_sqe **sqes;
17 struct io_uring_cqe *cqes;
18 int nr;
19 };
20
free_context(struct test_context * ctx)21 static void free_context(struct test_context *ctx)
22 {
23 free(ctx->sqes);
24 free(ctx->cqes);
25 memset(ctx, 0, sizeof(*ctx));
26 }
27
init_context(struct test_context * ctx,struct io_uring * ring,int nr)28 static 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;
36 ctx->sqes = t_malloc(nr * sizeof(*ctx->sqes));
37 ctx->cqes = t_malloc(nr * sizeof(*ctx->cqes));
38
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;
52 err:
53 free_context(ctx);
54 printf("init context failed\n");
55 return 1;
56 }
57
wait_cqes(struct test_context * ctx)58 static 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
test_cancelled_userdata(struct io_uring * ring)77 static 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
97 for (i = 0; i < nr; i++) {
98 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;
106 err:
107 free_context(&ctx);
108 return 1;
109 }
110
test_thread_link_cancel(struct io_uring * ring)111 static 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
131 for (i = 0; i < nr; i++) {
132 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;
147 err:
148 free_context(&ctx);
149 return 1;
150 }
151
test_drain_with_linked_timeout(struct io_uring * ring)152 static 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;
179 err:
180 free_context(&ctx);
181 return 1;
182 }
183
run_drained(struct io_uring * ring,int nr)184 static 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;
206 err:
207 free_context(&ctx);
208 return 1;
209 }
210
test_overflow_hung(struct io_uring * ring)211 static int test_overflow_hung(struct io_uring *ring)
212 {
213 struct io_uring_sqe *sqe;
214 int ret, nr = 10;
215
216 while (*ring->cq.koverflow != 1000) {
217 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
test_dropped_hung(struct io_uring * ring)234 static 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
main(int argc,char * argv[])242 int main(int argc, char *argv[])
243 {
244 struct io_uring ring, poll_ring, sqthread_ring;
245 struct io_uring_params p;
246 int ret, no_sqthread = 0;
247
248 if (argc > 1)
249 return 0;
250
251 memset(&p, 0, sizeof(p));
252 ret = io_uring_queue_init_params(1000, &ring, &p);
253 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 = t_create_ring(1000, &sqthread_ring,
265 IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL);
266 if (ret == T_SETUP_SKIP)
267 return 0;
268 else if (ret < 0)
269 return 1;
270
271 ret = test_cancelled_userdata(&poll_ring);
272 if (ret) {
273 printf("test_cancelled_userdata failed\n");
274 return ret;
275 }
276
277 if (no_sqthread) {
278 printf("test_thread_link_cancel: skipped, not root\n");
279 } else {
280 ret = test_thread_link_cancel(&sqthread_ring);
281 if (ret) {
282 printf("test_thread_link_cancel failed\n");
283 return ret;
284 }
285 }
286
287 if (!(p.features & IORING_FEAT_NODROP)) {
288 ret = test_overflow_hung(&ring);
289 if (ret) {
290 printf("test_overflow_hung failed\n");
291 return ret;
292 }
293 }
294
295 ret = test_dropped_hung(&ring);
296 if (ret) {
297 printf("test_dropped_hung failed\n");
298 return ret;
299 }
300
301 ret = test_drain_with_linked_timeout(&ring);
302 if (ret) {
303 printf("test_drain_with_linked_timeout failed\n");
304 return ret;
305 }
306
307 return 0;
308 }
309