• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: MIT
2 
3 #include <errno.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <signal.h>
9 #include <poll.h>
10 #include <sys/wait.h>
11 
12 #include "liburing.h"
13 #include "helpers.h"
14 
check_final_cqe(struct io_uring * ring)15 static int check_final_cqe(struct io_uring *ring)
16 {
17 	struct io_uring_cqe *cqe;
18 	int count = 0;
19 	bool signalled_no_more = false;
20 
21 	while (!io_uring_peek_cqe(ring, &cqe)) {
22 		if (cqe->user_data == 1) {
23 			count++;
24 			if (signalled_no_more) {
25 				fprintf(stderr, "signalled no more!\n");
26 				return T_EXIT_FAIL;
27 			}
28 			if (!(cqe->flags & IORING_CQE_F_MORE))
29 				signalled_no_more = true;
30 		} else if (cqe->user_data != 3) {
31 			fprintf(stderr, "%d: got unexpected %d\n", count, (int)cqe->user_data);
32 			return T_EXIT_FAIL;
33 		}
34 		io_uring_cqe_seen(ring, cqe);
35 	}
36 
37 	if (!count) {
38 		fprintf(stderr, "no cqe\n");
39 		return T_EXIT_FAIL;
40 	}
41 
42 	return T_EXIT_PASS;
43 }
44 
test(bool defer_taskrun)45 static int test(bool defer_taskrun)
46 {
47 	struct io_uring_cqe *cqe;
48 	struct io_uring_sqe *sqe;
49 	struct io_uring ring;
50 	int pipe1[2];
51 	int ret, i;
52 
53 	if (pipe(pipe1) != 0) {
54 		perror("pipe");
55 		return T_EXIT_FAIL;
56 	}
57 
58 	struct io_uring_params params = {
59 		/* cheat using SINGLE_ISSUER existence to know if this behaviour
60 		 * is updated
61 		 */
62 		.flags = IORING_SETUP_CQSIZE | IORING_SETUP_SINGLE_ISSUER,
63 		.cq_entries = 2
64 	};
65 
66 	if (defer_taskrun)
67 		params.flags |= IORING_SETUP_SINGLE_ISSUER |
68 				IORING_SETUP_DEFER_TASKRUN;
69 
70 	ret = io_uring_queue_init_params(2, &ring, &params);
71 	if (ret)
72 		return T_EXIT_SKIP;
73 
74 	sqe = io_uring_get_sqe(&ring);
75 	if (!sqe) {
76 		fprintf(stderr, "get sqe failed\n");
77 		return T_EXIT_FAIL;
78 	}
79 	io_uring_prep_poll_multishot(sqe, pipe1[0], POLLIN);
80 	io_uring_sqe_set_data64(sqe, 1);
81 
82 	if (io_uring_cq_ready(&ring)) {
83 		fprintf(stderr, "unexpected cqe\n");
84 		return T_EXIT_FAIL;
85 	}
86 
87 	for (i = 0; i < 2; i++) {
88 		sqe = io_uring_get_sqe(&ring);
89 		io_uring_prep_nop(sqe);
90 		io_uring_sqe_set_data64(sqe, 2);
91 		io_uring_submit(&ring);
92 	}
93 
94 	do {
95 		errno = 0;
96 		ret = write(pipe1[1], "foo", 3);
97 	} while (ret == -1 && errno == EINTR);
98 
99 	if (ret <= 0) {
100 		fprintf(stderr, "write failed: %d\n", errno);
101 		return T_EXIT_FAIL;
102 	}
103 
104 	/* should have 2 cqe + 1 overflow now, so take out two cqes */
105 	for (i = 0; i < 2; i++) {
106 		if (io_uring_peek_cqe(&ring, &cqe)) {
107 			fprintf(stderr, "unexpectedly no cqe\n");
108 			return T_EXIT_FAIL;
109 		}
110 		if (cqe->user_data != 2) {
111 			fprintf(stderr, "unexpected user_data\n");
112 			return T_EXIT_FAIL;
113 		}
114 		io_uring_cqe_seen(&ring, cqe);
115 	}
116 
117 	/* make sure everything is processed */
118 	io_uring_get_events(&ring);
119 
120 	/* now remove the poll */
121 	sqe = io_uring_get_sqe(&ring);
122 	io_uring_prep_poll_remove(sqe, 1);
123 	io_uring_sqe_set_data64(sqe, 3);
124 	ret = io_uring_submit(&ring);
125 
126 	if (ret != 1) {
127 		fprintf(stderr, "bad poll remove\n");
128 		return T_EXIT_FAIL;
129 	}
130 
131 	ret = check_final_cqe(&ring);
132 
133 	close(pipe1[0]);
134 	close(pipe1[1]);
135 	io_uring_queue_exit(&ring);
136 
137 	return ret;
138 }
139 
test_downgrade(bool support_defer)140 static int test_downgrade(bool support_defer)
141 {
142 	struct io_uring_cqe cqes[128];
143 	struct io_uring_cqe *cqe;
144 	struct io_uring_sqe *sqe;
145 	struct io_uring ring;
146 	int fds[2];
147 	int ret, i, cqe_count, tmp = 0, more_cqe_count;
148 
149 	if (pipe(fds) != 0) {
150 		perror("pipe");
151 		return -1;
152 	}
153 
154 	struct io_uring_params params = {
155 		.flags = IORING_SETUP_CQSIZE,
156 		.cq_entries = 2
157 	};
158 
159 	ret = io_uring_queue_init_params(2, &ring, &params);
160 	if (ret) {
161 		fprintf(stderr, "queue init: %d\n", ret);
162 		return -1;
163 	}
164 
165 	sqe = io_uring_get_sqe(&ring);
166 	if (!sqe) {
167 		fprintf(stderr, "get sqe failed\n");
168 		return -1;
169 	}
170 	io_uring_prep_poll_multishot(sqe, fds[0], POLLIN);
171 	io_uring_sqe_set_data64(sqe, 1);
172 	io_uring_submit(&ring);
173 
174 	for (i = 0; i < 8; i++) {
175 		ret = write(fds[1], &tmp, sizeof(tmp));
176 		if (ret != sizeof(tmp)) {
177 			perror("write");
178 			return -1;
179 		}
180 		ret = read(fds[0], &tmp, sizeof(tmp));
181 		if (ret != sizeof(tmp)) {
182 			perror("read");
183 			return -1;
184 		}
185 	}
186 
187 	cqe_count = 0;
188 	while (!io_uring_peek_cqe(&ring, &cqe)) {
189 		cqes[cqe_count++] = *cqe;
190 		io_uring_cqe_seen(&ring, cqe);
191 	}
192 
193 	/* Some kernels might allow overflows to poll,
194 	 * but if they didn't it should stop the MORE flag
195 	 */
196 	if (cqe_count < 3) {
197 		fprintf(stderr, "too few cqes: %d\n", cqe_count);
198 		return -1;
199 	} else if (cqe_count == 8) {
200 		more_cqe_count = cqe_count;
201 		/* downgrade only available since support_defer */
202 		if (support_defer) {
203 			fprintf(stderr, "did not downgrade on overflow\n");
204 			return -1;
205 		}
206 	} else {
207 		more_cqe_count = cqe_count - 1;
208 		cqe = &cqes[cqe_count - 1];
209 		if (cqe->flags & IORING_CQE_F_MORE) {
210 			fprintf(stderr, "incorrect MORE flag %x\n", cqe->flags);
211 			return -1;
212 		}
213 	}
214 
215 	for (i = 0; i < more_cqe_count; i++) {
216 		cqe = &cqes[i];
217 		if (!(cqe->flags & IORING_CQE_F_MORE)) {
218 			fprintf(stderr, "missing MORE flag\n");
219 			return -1;
220 		}
221 		if (cqe->res < 0) {
222 			fprintf(stderr, "bad res: %d\n", cqe->res);
223 			return -1;
224 		}
225 	}
226 
227 	close(fds[0]);
228 	close(fds[1]);
229 	io_uring_queue_exit(&ring);
230 	return 0;
231 }
232 
main(int argc,char * argv[])233 int main(int argc, char *argv[])
234 {
235 	int ret;
236 	bool support_defer;
237 
238 	if (argc > 1)
239 		return T_EXIT_SKIP;
240 
241 	support_defer = t_probe_defer_taskrun();
242 	ret = test_downgrade(support_defer);
243 	if (ret) {
244 		fprintf(stderr, "%s: test_downgrade(%d) failed\n", argv[0], support_defer);
245 		return T_EXIT_FAIL;
246 	}
247 
248 	ret = test(false);
249 	if (ret == T_EXIT_SKIP)
250 		return ret;
251 	if (ret != T_EXIT_PASS) {
252 		fprintf(stderr, "%s: test(false) failed\n", argv[0]);
253 		return ret;
254 	}
255 
256 	if (support_defer) {
257 		ret = test(true);
258 		if (ret != T_EXIT_PASS) {
259 			fprintf(stderr, "%s: test(true) failed\n", argv[0]);
260 			return ret;
261 		}
262 	}
263 
264 	return ret;
265 }
266