• 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 <fcntl.h>
13 #include <poll.h>
14 #include <sys/wait.h>
15 #include <sys/select.h>
16 #include <pthread.h>
17 #include <sys/epoll.h>
18 
19 #include "liburing.h"
20 #include "helpers.h"
21 
22 struct thread_data {
23 	struct io_uring *ring;
24 	int fd;
25 	int events;
26 	const char *test;
27 	int out[2];
28 };
29 
epoll_wait_fn(void * data)30 static void *epoll_wait_fn(void *data)
31 {
32 	struct thread_data *td = data;
33 	struct epoll_event ev;
34 
35 	if (epoll_wait(td->fd, &ev, 1, -1) < 0) {
36 		perror("epoll_wait");
37 		goto err;
38 	}
39 
40 	return NULL;
41 err:
42 	return (void *) 1;
43 }
44 
iou_poll(void * data)45 static void *iou_poll(void *data)
46 {
47 	struct thread_data *td = data;
48 	struct io_uring_sqe *sqe;
49 	struct io_uring_cqe *cqe;
50 	int ret;
51 
52 	sqe = io_uring_get_sqe(td->ring);
53 	io_uring_prep_poll_add(sqe, td->fd, td->events);
54 
55 	ret = io_uring_submit(td->ring);
56 	if (ret != 1) {
57 		fprintf(stderr, "submit got %d\n", ret);
58 		goto err;
59 	}
60 
61 	ret = io_uring_wait_cqe(td->ring, &cqe);
62 	if (ret) {
63 		fprintf(stderr, "wait_cqe: %d\n", ret);
64 		goto err;
65 	}
66 
67 	td->out[0] = cqe->res & 0x3f;
68 	io_uring_cqe_seen(td->ring, cqe);
69 	return NULL;
70 err:
71 	return (void *) 1;
72 }
73 
poll_pipe(void * data)74 static void *poll_pipe(void *data)
75 {
76 	struct thread_data *td = data;
77 	struct pollfd pfd;
78 	int ret;
79 
80 	pfd.fd = td->fd;
81 	pfd.events = td->events;
82 
83 	ret = poll(&pfd, 1, -1);
84 	if (ret < 0)
85 		perror("poll");
86 
87 	td->out[1] = pfd.revents;
88 	return NULL;
89 }
90 
do_pipe_pollin_test(struct io_uring * ring)91 static int do_pipe_pollin_test(struct io_uring *ring)
92 {
93 	struct thread_data td;
94 	pthread_t threads[2];
95 	int ret, pipe1[2];
96 	char buf;
97 
98 	if (pipe(pipe1) < 0) {
99 		perror("pipe");
100 		return 1;
101 	}
102 
103 	td.ring = ring;
104 	td.fd = pipe1[0];
105 	td.events = POLLIN;
106 	td.test = __FUNCTION__;
107 
108 	pthread_create(&threads[1], NULL, iou_poll, &td);
109 	pthread_create(&threads[0], NULL, poll_pipe, &td);
110 	usleep(100000);
111 
112 	buf = 0x89;
113 	ret = write(pipe1[1], &buf, sizeof(buf));
114 	if (ret != sizeof(buf)) {
115 		fprintf(stderr, "write failed: %d\n", ret);
116 		return 1;
117 	}
118 
119 	pthread_join(threads[0], NULL);
120 	pthread_join(threads[1], NULL);
121 
122 	if (td.out[0] != td.out[1]) {
123 		fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
124 							td.out[0], td.out[1]);
125 		return 1;
126 	}
127 	return 0;
128 }
129 
do_pipe_pollout_test(struct io_uring * ring)130 static int do_pipe_pollout_test(struct io_uring *ring)
131 {
132 	struct thread_data td;
133 	pthread_t threads[2];
134 	int ret, pipe1[2];
135 	char buf;
136 
137 	if (pipe(pipe1) < 0) {
138 		perror("pipe");
139 		return 1;
140 	}
141 
142 	td.ring = ring;
143 	td.fd = pipe1[1];
144 	td.events = POLLOUT;
145 	td.test = __FUNCTION__;
146 
147 	pthread_create(&threads[0], NULL, poll_pipe, &td);
148 	pthread_create(&threads[1], NULL, iou_poll, &td);
149 	usleep(100000);
150 
151 	buf = 0x89;
152 	ret = write(pipe1[1], &buf, sizeof(buf));
153 	if (ret != sizeof(buf)) {
154 		fprintf(stderr, "write failed: %d\n", ret);
155 		return 1;
156 	}
157 
158 	pthread_join(threads[0], NULL);
159 	pthread_join(threads[1], NULL);
160 
161 	if (td.out[0] != td.out[1]) {
162 		fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
163 							td.out[0], td.out[1]);
164 		return 1;
165 	}
166 
167 	return 0;
168 }
169 
do_fd_test(struct io_uring * ring,const char * fname,int events)170 static int do_fd_test(struct io_uring *ring, const char *fname, int events)
171 {
172 	struct thread_data td;
173 	pthread_t threads[2];
174 	int fd;
175 
176 	fd = open(fname, O_RDONLY);
177 	if (fd < 0) {
178 		if (errno == EPERM || errno == EACCES)
179 			return T_EXIT_SKIP;
180 		perror("open");
181 		return 1;
182 	}
183 
184 	td.ring = ring;
185 	td.fd = fd;
186 	td.events = events;
187 	td.test = __FUNCTION__;
188 
189 	pthread_create(&threads[0], NULL, poll_pipe, &td);
190 	pthread_create(&threads[1], NULL, iou_poll, &td);
191 
192 	pthread_join(threads[0], NULL);
193 	pthread_join(threads[1], NULL);
194 
195 	if (td.out[0] != td.out[1]) {
196 		fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
197 							td.out[0], td.out[1]);
198 		return 1;
199 	}
200 
201 	return 0;
202 }
203 
iou_epoll_ctl(struct io_uring * ring,int epfd,int fd,struct epoll_event * ev)204 static int iou_epoll_ctl(struct io_uring *ring, int epfd, int fd,
205 			 struct epoll_event *ev)
206 {
207 	struct io_uring_sqe *sqe;
208 	struct io_uring_cqe *cqe;
209 	int ret;
210 
211 	sqe = io_uring_get_sqe(ring);
212 	if (!sqe) {
213 		fprintf(stderr, "Failed to get sqe\n");
214 		return 1;
215 	}
216 
217 	io_uring_prep_epoll_ctl(sqe, epfd, fd, EPOLL_CTL_ADD, ev);
218 
219 	ret = io_uring_submit(ring);
220 	if (ret != 1) {
221 		fprintf(stderr, "submit: %d\n", ret);
222 		return 1;
223 	}
224 
225 	ret = io_uring_wait_cqe(ring, &cqe);
226 	if (ret) {
227 		fprintf(stderr, "wait_cqe: %d\n", ret);
228 		return 1;
229 	}
230 
231 	ret = cqe->res;
232 	io_uring_cqe_seen(ring, cqe);
233 	return ret;
234 }
235 
do_test_epoll(struct io_uring * ring,int iou_epoll_add)236 static int do_test_epoll(struct io_uring *ring, int iou_epoll_add)
237 {
238 	struct epoll_event ev;
239 	struct thread_data td;
240 	pthread_t threads[2];
241 	int ret, pipe1[2];
242 	char buf;
243 	int fd;
244 
245 	fd = epoll_create1(0);
246 	if (fd < 0) {
247 		perror("epoll_create");
248 		return 1;
249 	}
250 
251 	if (pipe(pipe1) < 0) {
252 		perror("pipe");
253 		return 1;
254 	}
255 
256 	ev.events = EPOLLIN;
257 	ev.data.fd = pipe1[0];
258 
259 	if (!iou_epoll_add) {
260 		if (epoll_ctl(fd, EPOLL_CTL_ADD, pipe1[0], &ev) < 0) {
261 			perror("epoll_ctrl");
262 			return 1;
263 		}
264 	} else {
265 		ret = iou_epoll_ctl(ring, fd, pipe1[0], &ev);
266 		if (ret == -EINVAL) {
267 			fprintf(stdout, "epoll not supported, skipping\n");
268 			return 0;
269 		} else if (ret < 0) {
270 			return 1;
271 		}
272 	}
273 
274 	td.ring = ring;
275 	td.fd = fd;
276 	td.events = POLLIN;
277 	td.test = __FUNCTION__;
278 
279 	pthread_create(&threads[0], NULL, iou_poll, &td);
280 	pthread_create(&threads[1], NULL, epoll_wait_fn, &td);
281 	usleep(100000);
282 
283 	buf = 0x89;
284 	ret = write(pipe1[1], &buf, sizeof(buf));
285 	if (ret != sizeof(buf)) {
286 		fprintf(stderr, "write failed: %d\n", ret);
287 		return 1;
288 	}
289 
290 	pthread_join(threads[0], NULL);
291 	pthread_join(threads[1], NULL);
292 	return 0;
293 }
294 
main(int argc,char * argv[])295 int main(int argc, char *argv[])
296 {
297 	struct io_uring ring;
298 	const char *fname;
299 	int ret;
300 
301 	ret = io_uring_queue_init(1, &ring, 0);
302 	if (ret) {
303 		fprintf(stderr, "ring setup failed\n");
304 		return 1;
305 	}
306 
307 	ret = do_pipe_pollin_test(&ring);
308 	if (ret) {
309 		fprintf(stderr, "pipe pollin test failed\n");
310 		return ret;
311 	}
312 
313 	ret = do_pipe_pollout_test(&ring);
314 	if (ret) {
315 		fprintf(stderr, "pipe pollout test failed\n");
316 		return ret;
317 	}
318 
319 	ret = do_test_epoll(&ring, 0);
320 	if (ret) {
321 		fprintf(stderr, "epoll test 0 failed\n");
322 		return ret;
323 	}
324 
325 	ret = do_test_epoll(&ring, 1);
326 	if (ret) {
327 		fprintf(stderr, "epoll test 1 failed\n");
328 		return ret;
329 	}
330 
331 	if (argc > 1)
332 		fname = argv[1];
333 	else
334 		fname = argv[0];
335 
336 	ret = do_fd_test(&ring, fname, POLLIN);
337 	if (ret == T_EXIT_FAIL) {
338 		fprintf(stderr, "fd test IN failed\n");
339 		return ret;
340 	}
341 
342 	ret = do_fd_test(&ring, fname, POLLOUT);
343 	if (ret == T_EXIT_FAIL) {
344 		fprintf(stderr, "fd test OUT failed\n");
345 		return ret;
346 	}
347 
348 	ret = do_fd_test(&ring, fname, POLLOUT | POLLIN);
349 	if (ret == T_EXIT_FAIL) {
350 		fprintf(stderr, "fd test IN|OUT failed\n");
351 		return ret;
352 	}
353 
354 	return 0;
355 
356 }
357