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