• 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 #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