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