• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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