• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: test io_uring poll handling
4  *
5  */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <signal.h>
12 #include <poll.h>
13 #include <sys/wait.h>
14 #include <assert.h>
15 
16 #include "helpers.h"
17 #include "liburing.h"
18 
do_setsockopt(int fd,int level,int optname,int val)19 static void do_setsockopt(int fd, int level, int optname, int val)
20 {
21 	if (setsockopt(fd, level, optname, &val, sizeof(val)))
22 		t_error(1, errno, "setsockopt %d.%d: %d", level, optname, val);
23 }
24 
check_cq_empty(struct io_uring * ring)25 static bool check_cq_empty(struct io_uring *ring)
26 {
27 	struct io_uring_cqe *cqe = NULL;
28 	int ret;
29 
30 	ret = io_uring_peek_cqe(ring, &cqe); /* nothing should be there */
31 	return ret == -EAGAIN;
32 }
33 
test_basic(void)34 static int test_basic(void)
35 {
36 	struct io_uring_cqe *cqe;
37 	struct io_uring_sqe *sqe;
38 	struct io_uring ring;
39 	int pipe1[2];
40 	pid_t p;
41 	int ret;
42 
43 	if (pipe(pipe1) != 0) {
44 		perror("pipe");
45 		return 1;
46 	}
47 
48 	p = fork();
49 	if (p == -1) {
50 		perror("fork");
51 		exit(2);
52 	} else if (p == 0) {
53 		ret = io_uring_queue_init(1, &ring, 0);
54 		if (ret) {
55 			fprintf(stderr, "child: ring setup failed: %d\n", ret);
56 			return 1;
57 		}
58 
59 		sqe = io_uring_get_sqe(&ring);
60 		if (!sqe) {
61 			fprintf(stderr, "get sqe failed\n");
62 			return 1;
63 		}
64 
65 		io_uring_prep_poll_add(sqe, pipe1[0], POLLIN);
66 		io_uring_sqe_set_data(sqe, sqe);
67 
68 		ret = io_uring_submit(&ring);
69 		if (ret <= 0) {
70 			fprintf(stderr, "child: sqe submit failed: %d\n", ret);
71 			return 1;
72 		}
73 
74 		do {
75 			ret = io_uring_wait_cqe(&ring, &cqe);
76 			if (ret < 0) {
77 				fprintf(stderr, "child: wait completion %d\n", ret);
78 				break;
79 			}
80 			io_uring_cqe_seen(&ring, cqe);
81 		} while (ret != 0);
82 
83 		if (ret < 0)
84 			return 1;
85 		if (cqe->user_data != (unsigned long) sqe) {
86 			fprintf(stderr, "child: cqe doesn't match sqe\n");
87 			return 1;
88 		}
89 		if ((cqe->res & POLLIN) != POLLIN) {
90 			fprintf(stderr, "child: bad return value %ld\n",
91 							(long) cqe->res);
92 			return 1;
93 		}
94 
95 		io_uring_queue_exit(&ring);
96 		exit(0);
97 	}
98 
99 	do {
100 		errno = 0;
101 		ret = write(pipe1[1], "foo", 3);
102 	} while (ret == -1 && errno == EINTR);
103 
104 	if (ret != 3) {
105 		fprintf(stderr, "parent: bad write return %d\n", ret);
106 		return 1;
107 	}
108 	close(pipe1[0]);
109 	close(pipe1[1]);
110 	return 0;
111 }
112 
test_missing_events(void)113 static int test_missing_events(void)
114 {
115 	struct io_uring_cqe *cqe;
116 	struct io_uring_sqe *sqe;
117 	struct io_uring ring;
118 	int i, ret, sp[2];
119 	char buf[2] = {};
120 	int res_mask = 0;
121 
122 	ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
123 					    IORING_SETUP_DEFER_TASKRUN);
124 	if (ret) {
125 		fprintf(stderr, "ring setup failed: %d\n", ret);
126 		return 1;
127 	}
128 
129 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0) {
130 		perror("Failed to create Unix-domain socket pair\n");
131 		return 1;
132 	}
133 	do_setsockopt(sp[0], SOL_SOCKET, SO_SNDBUF, 1);
134 	ret = send(sp[0], buf, sizeof(buf), 0);
135 	if (ret != sizeof(buf)) {
136 		perror("send failed\n");
137 		return 1;
138 	}
139 
140 	sqe = io_uring_get_sqe(&ring);
141 	io_uring_prep_poll_multishot(sqe, sp[0], POLLIN|POLLOUT);
142 	ret = io_uring_submit(&ring);
143 	if (ret != 1) {
144 		fprintf(stderr, "sqe submit failed: %d\n", ret);
145 		return 1;
146 	}
147 
148 	/* trigger EPOLLIN */
149 	ret = send(sp[1], buf, sizeof(buf), 0);
150 	if (ret != sizeof(buf)) {
151 		fprintf(stderr, "send sp[1] failed %i %i\n", ret, errno);
152 		return 1;
153 	}
154 
155 	/* trigger EPOLLOUT */
156 	ret = recv(sp[1], buf, sizeof(buf), 0);
157 	if (ret != sizeof(buf)) {
158 		perror("recv failed\n");
159 		return 1;
160 	}
161 
162 	for (i = 0; ; i++) {
163 		if (i == 0)
164 			ret = io_uring_wait_cqe(&ring, &cqe);
165 		else
166 			ret = io_uring_peek_cqe(&ring, &cqe);
167 
168 		if (i != 0 && ret == -EAGAIN) {
169 			break;
170 		}
171 		if (ret) {
172 			fprintf(stderr, "wait completion %d, %i\n", ret, i);
173 			return 1;
174 		}
175 		res_mask |= cqe->res;
176 		io_uring_cqe_seen(&ring, cqe);
177 	}
178 
179 	if ((res_mask & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
180 		fprintf(stderr, "missing poll events %i\n", res_mask);
181 		return 1;
182 	}
183 	io_uring_queue_exit(&ring);
184 	close(sp[0]);
185 	close(sp[1]);
186 	return 0;
187 }
188 
189 #define NR_SQES		2048
190 
test_self_poll(void)191 static int test_self_poll(void)
192 {
193 	struct io_uring_cqe *cqe;
194 	struct io_uring_sqe *sqe;
195 	struct io_uring ring;
196 	int ret, i, j;
197 
198 	ret = io_uring_queue_init(NR_SQES, &ring, 0);
199 	if (ret) {
200 		fprintf(stderr, "ring setup failed: %d\n", ret);
201 		return T_EXIT_FAIL;
202 	}
203 
204 	for (j = 0; j < 32; j++) {
205 		for (i = 0; i < NR_SQES; i++) {
206 			sqe = io_uring_get_sqe(&ring);
207 			io_uring_prep_poll_add(sqe, ring.ring_fd, POLLIN);
208 		}
209 
210 		ret = io_uring_submit(&ring);
211 		assert(ret == NR_SQES);
212 	}
213 
214 	sqe = io_uring_get_sqe(&ring);
215 	io_uring_prep_nop(sqe);
216 	ret = io_uring_submit(&ring);
217 	assert(ret == 1);
218 
219 	ret = io_uring_wait_cqe(&ring, &cqe);
220 	io_uring_cqe_seen(&ring, cqe);
221 
222 	io_uring_queue_exit(&ring);
223 	return T_EXIT_PASS;
224 }
225 
test_disabled_ring_lazy_polling(int early_poll)226 static int test_disabled_ring_lazy_polling(int early_poll)
227 {
228 	struct io_uring_cqe *cqe;
229 	struct io_uring_sqe *sqe;
230 	struct io_uring ring, ring2;
231 	unsigned head;
232 	int ret, i = 0;
233 
234 	ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
235 					     IORING_SETUP_DEFER_TASKRUN |
236 					     IORING_SETUP_R_DISABLED);
237 	if (ret) {
238 		fprintf(stderr, "ring setup failed: %d\n", ret);
239 		return 1;
240 	}
241 	ret = io_uring_queue_init(8, &ring2, 0);
242 	if (ret) {
243 		fprintf(stderr, "ring2 setup failed: %d\n", ret);
244 		return 1;
245 	}
246 
247 	if (early_poll) {
248 		/* start polling disabled DEFER_TASKRUN ring */
249 		sqe = io_uring_get_sqe(&ring2);
250 		io_uring_prep_poll_add(sqe, ring.ring_fd, POLLIN);
251 		ret = io_uring_submit(&ring2);
252 		assert(ret == 1);
253 		assert(check_cq_empty(&ring2));
254 	}
255 
256 	/* enable rings, which should also activate pollwq */
257 	ret = io_uring_enable_rings(&ring);
258 	assert(ret >= 0);
259 
260 	if (!early_poll) {
261 		/* start polling enabled DEFER_TASKRUN ring */
262 		sqe = io_uring_get_sqe(&ring2);
263 		io_uring_prep_poll_add(sqe, ring.ring_fd, POLLIN);
264 		ret = io_uring_submit(&ring2);
265 		assert(ret == 1);
266 		assert(check_cq_empty(&ring2));
267 	}
268 
269 	sqe = io_uring_get_sqe(&ring);
270 	io_uring_prep_nop(sqe);
271 	ret = io_uring_submit(&ring);
272 	assert(ret == 1);
273 
274 	io_uring_for_each_cqe(&ring2, head, cqe) {
275 		i++;
276 	}
277 	if (i !=  1) {
278 		fprintf(stderr, "fail, polling stuck\n");
279 		return 1;
280 	}
281 	io_uring_queue_exit(&ring);
282 	io_uring_queue_exit(&ring2);
283 	return 0;
284 }
285 
main(int argc,char * argv[])286 int main(int argc, char *argv[])
287 {
288 	int ret;
289 
290 	if (argc > 1)
291 		return 0;
292 
293 	ret = test_basic();
294 	if (ret) {
295 		fprintf(stderr, "test_basic() failed %i\n", ret);
296 		return T_EXIT_FAIL;
297 	}
298 
299 
300 	if (t_probe_defer_taskrun()) {
301 		ret = test_missing_events();
302 		if (ret) {
303 			fprintf(stderr, "test_missing_events() failed %i\n", ret);
304 			return T_EXIT_FAIL;
305 		}
306 
307 		ret = test_disabled_ring_lazy_polling(false);
308 		if (ret) {
309 			fprintf(stderr, "test_disabled_ring_lazy_polling(false) failed %i\n", ret);
310 			return T_EXIT_FAIL;
311 		}
312 
313 		ret = test_disabled_ring_lazy_polling(true);
314 		if (ret) {
315 			fprintf(stderr, "test_disabled_ring_lazy_polling(true) failed %i\n", ret);
316 			return T_EXIT_FAIL;
317 		}
318 	}
319 
320 	ret = test_self_poll();
321 	if (ret) {
322 		fprintf(stderr, "test_self_poll failed\n");
323 		return T_EXIT_FAIL;
324 	}
325 
326 	return 0;
327 }
328