1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Check that IORING_OP_ACCEPT works, and send some data across to verify we
4 * didn't get a junk fd.
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <assert.h>
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <sys/socket.h>
15 #include <sys/time.h>
16 #include <sys/resource.h>
17 #include <sys/un.h>
18 #include <netinet/tcp.h>
19 #include <netinet/in.h>
20
21 #include "helpers.h"
22 #include "liburing.h"
23
24 static int no_accept;
25
26 struct data {
27 char buf[128];
28 struct iovec iov;
29 };
30
queue_send(struct io_uring * ring,int fd)31 static void queue_send(struct io_uring *ring, int fd)
32 {
33 struct io_uring_sqe *sqe;
34 struct data *d;
35
36 d = t_malloc(sizeof(*d));
37 d->iov.iov_base = d->buf;
38 d->iov.iov_len = sizeof(d->buf);
39
40 sqe = io_uring_get_sqe(ring);
41 io_uring_prep_writev(sqe, fd, &d->iov, 1, 0);
42 }
43
queue_recv(struct io_uring * ring,int fd)44 static void queue_recv(struct io_uring *ring, int fd)
45 {
46 struct io_uring_sqe *sqe;
47 struct data *d;
48
49 d = t_malloc(sizeof(*d));
50 d->iov.iov_base = d->buf;
51 d->iov.iov_len = sizeof(d->buf);
52
53 sqe = io_uring_get_sqe(ring);
54 io_uring_prep_readv(sqe, fd, &d->iov, 1, 0);
55 }
56
accept_conn(struct io_uring * ring,int fd)57 static int accept_conn(struct io_uring *ring, int fd)
58 {
59 struct io_uring_sqe *sqe;
60 struct io_uring_cqe *cqe;
61 int ret;
62
63 sqe = io_uring_get_sqe(ring);
64 io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
65
66 ret = io_uring_submit(ring);
67 assert(ret != -1);
68
69 ret = io_uring_wait_cqe(ring, &cqe);
70 assert(!ret);
71 ret = cqe->res;
72 io_uring_cqe_seen(ring, cqe);
73 return ret;
74 }
75
start_accept_listen(struct sockaddr_in * addr,int port_off)76 static int start_accept_listen(struct sockaddr_in *addr, int port_off)
77 {
78 int fd, ret;
79
80 fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
81
82 int32_t val = 1;
83 ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
84 assert(ret != -1);
85 ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
86 assert(ret != -1);
87
88 struct sockaddr_in laddr;
89
90 if (!addr)
91 addr = &laddr;
92
93 addr->sin_family = AF_INET;
94 addr->sin_port = 0x1235 + port_off;
95 addr->sin_addr.s_addr = 0x0100007fU;
96
97 ret = bind(fd, (struct sockaddr*)addr, sizeof(*addr));
98 assert(ret != -1);
99 ret = listen(fd, 128);
100 assert(ret != -1);
101
102 return fd;
103 }
104
test(struct io_uring * ring,int accept_should_error)105 static int test(struct io_uring *ring, int accept_should_error)
106 {
107 struct io_uring_cqe *cqe;
108 struct sockaddr_in addr;
109 uint32_t head;
110 uint32_t count = 0;
111 int done = 0;
112 int p_fd[2];
113 int ret;
114
115 int32_t val, recv_s0 = start_accept_listen(&addr, 0);
116
117 p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
118
119 val = 1;
120 ret = setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
121 assert(ret != -1);
122
123 int32_t flags = fcntl(p_fd[1], F_GETFL, 0);
124 assert(flags != -1);
125
126 flags |= O_NONBLOCK;
127 ret = fcntl(p_fd[1], F_SETFL, flags);
128 assert(ret != -1);
129
130 ret = connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr));
131 assert(ret == -1);
132
133 flags = fcntl(p_fd[1], F_GETFL, 0);
134 assert(flags != -1);
135
136 flags &= ~O_NONBLOCK;
137 ret = fcntl(p_fd[1], F_SETFL, flags);
138 assert(ret != -1);
139
140 p_fd[0] = accept_conn(ring, recv_s0);
141 if (p_fd[0] == -EINVAL) {
142 if (accept_should_error)
143 goto out;
144 fprintf(stdout, "Accept not supported, skipping\n");
145 no_accept = 1;
146 goto out;
147 } else if (p_fd[0] < 0) {
148 if (accept_should_error &&
149 (p_fd[0] == -EBADF || p_fd[0] == -EINVAL))
150 goto out;
151 fprintf(stderr, "Accept got %d\n", p_fd[0]);
152 goto err;
153 }
154
155 queue_send(ring, p_fd[1]);
156 queue_recv(ring, p_fd[0]);
157
158 ret = io_uring_submit_and_wait(ring, 2);
159 assert(ret != -1);
160
161 while (count < 2) {
162 io_uring_for_each_cqe(ring, head, cqe) {
163 if (cqe->res < 0) {
164 fprintf(stderr, "Got cqe res %d\n", cqe->res);
165 done = 1;
166 break;
167 }
168 assert(cqe->res == 128);
169 count++;
170 }
171
172 assert(count <= 2);
173 io_uring_cq_advance(ring, count);
174 if (done)
175 goto err;
176 }
177
178 out:
179 close(p_fd[0]);
180 close(p_fd[1]);
181 close(recv_s0);
182 return 0;
183 err:
184 close(p_fd[0]);
185 close(p_fd[1]);
186 close(recv_s0);
187 return 1;
188 }
189
sig_alrm(int sig)190 static void sig_alrm(int sig)
191 {
192 exit(0);
193 }
194
test_accept_pending_on_exit(void)195 static int test_accept_pending_on_exit(void)
196 {
197 struct io_uring m_io_uring;
198 struct io_uring_cqe *cqe;
199 struct io_uring_sqe *sqe;
200 int fd, ret;
201
202 ret = io_uring_queue_init(32, &m_io_uring, 0);
203 assert(ret >= 0);
204
205 fd = start_accept_listen(NULL, 0);
206
207 sqe = io_uring_get_sqe(&m_io_uring);
208 io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
209 ret = io_uring_submit(&m_io_uring);
210 assert(ret != -1);
211
212 signal(SIGALRM, sig_alrm);
213 alarm(1);
214 ret = io_uring_wait_cqe(&m_io_uring, &cqe);
215 assert(!ret);
216 io_uring_cqe_seen(&m_io_uring, cqe);
217
218 io_uring_queue_exit(&m_io_uring);
219 return 0;
220 }
221
222 /*
223 * Test issue many accepts and see if we handle cancellation on exit
224 */
test_accept_many(unsigned nr,unsigned usecs)225 static int test_accept_many(unsigned nr, unsigned usecs)
226 {
227 struct io_uring m_io_uring;
228 struct io_uring_cqe *cqe;
229 struct io_uring_sqe *sqe;
230 unsigned long cur_lim;
231 struct rlimit rlim;
232 int *fds, i, ret;
233
234 if (getrlimit(RLIMIT_NPROC, &rlim) < 0) {
235 perror("getrlimit");
236 return 1;
237 }
238
239 cur_lim = rlim.rlim_cur;
240 rlim.rlim_cur = nr / 4;
241
242 if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
243 perror("setrlimit");
244 return 1;
245 }
246
247 ret = io_uring_queue_init(2 * nr, &m_io_uring, 0);
248 assert(ret >= 0);
249
250 fds = t_calloc(nr, sizeof(int));
251
252 for (i = 0; i < nr; i++)
253 fds[i] = start_accept_listen(NULL, i);
254
255 for (i = 0; i < nr; i++) {
256 sqe = io_uring_get_sqe(&m_io_uring);
257 io_uring_prep_accept(sqe, fds[i], NULL, NULL, 0);
258 sqe->user_data = 1 + i;
259 ret = io_uring_submit(&m_io_uring);
260 assert(ret == 1);
261 }
262
263 if (usecs)
264 usleep(usecs);
265
266 for (i = 0; i < nr; i++) {
267 if (io_uring_peek_cqe(&m_io_uring, &cqe))
268 break;
269 if (cqe->res != -ECANCELED) {
270 fprintf(stderr, "Expected cqe to be cancelled\n");
271 goto err;
272 }
273 io_uring_cqe_seen(&m_io_uring, cqe);
274 }
275 out:
276 rlim.rlim_cur = cur_lim;
277 if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
278 perror("setrlimit");
279 return 1;
280 }
281
282 free(fds);
283 io_uring_queue_exit(&m_io_uring);
284 return 0;
285 err:
286 ret = 1;
287 goto out;
288 }
289
test_accept_cancel(unsigned usecs)290 static int test_accept_cancel(unsigned usecs)
291 {
292 struct io_uring m_io_uring;
293 struct io_uring_cqe *cqe;
294 struct io_uring_sqe *sqe;
295 int fd, i, ret;
296
297 ret = io_uring_queue_init(32, &m_io_uring, 0);
298 assert(ret >= 0);
299
300 fd = start_accept_listen(NULL, 0);
301
302 sqe = io_uring_get_sqe(&m_io_uring);
303 io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
304 sqe->user_data = 1;
305 ret = io_uring_submit(&m_io_uring);
306 assert(ret == 1);
307
308 if (usecs)
309 usleep(usecs);
310
311 sqe = io_uring_get_sqe(&m_io_uring);
312 io_uring_prep_cancel(sqe, (void *) 1, 0);
313 sqe->user_data = 2;
314 ret = io_uring_submit(&m_io_uring);
315 assert(ret == 1);
316
317 for (i = 0; i < 2; i++) {
318 ret = io_uring_wait_cqe(&m_io_uring, &cqe);
319 assert(!ret);
320 /*
321 * Two cases here:
322 *
323 * 1) We cancel the accept4() before it got started, we should
324 * get '0' for the cancel request and '-ECANCELED' for the
325 * accept request.
326 * 2) We cancel the accept4() after it's already running, we
327 * should get '-EALREADY' for the cancel request and
328 * '-EINTR' for the accept request.
329 */
330 if (cqe->user_data == 1) {
331 if (cqe->res != -EINTR && cqe->res != -ECANCELED) {
332 fprintf(stderr, "Cancelled accept got %d\n", cqe->res);
333 goto err;
334 }
335 } else if (cqe->user_data == 2) {
336 if (cqe->res != -EALREADY && cqe->res != 0) {
337 fprintf(stderr, "Cancel got %d\n", cqe->res);
338 goto err;
339 }
340 }
341 io_uring_cqe_seen(&m_io_uring, cqe);
342 }
343
344 io_uring_queue_exit(&m_io_uring);
345 return 0;
346 err:
347 io_uring_queue_exit(&m_io_uring);
348 return 1;
349 }
350
test_accept(void)351 static int test_accept(void)
352 {
353 struct io_uring m_io_uring;
354 int ret;
355
356 ret = io_uring_queue_init(32, &m_io_uring, 0);
357 assert(ret >= 0);
358 ret = test(&m_io_uring, 0);
359 io_uring_queue_exit(&m_io_uring);
360 return ret;
361 }
362
test_accept_sqpoll(void)363 static int test_accept_sqpoll(void)
364 {
365 struct io_uring m_io_uring;
366 struct io_uring_params p = { };
367 int ret, should_fail;
368
369 p.flags = IORING_SETUP_SQPOLL;
370 ret = t_create_ring_params(32, &m_io_uring, &p);
371 if (ret == T_SETUP_SKIP)
372 return 0;
373 else if (ret < 0)
374 return ret;
375
376 should_fail = 1;
377 if (p.features & IORING_FEAT_SQPOLL_NONFIXED)
378 should_fail = 0;
379
380 ret = test(&m_io_uring, should_fail);
381 io_uring_queue_exit(&m_io_uring);
382 return ret;
383 }
384
main(int argc,char * argv[])385 int main(int argc, char *argv[])
386 {
387 int ret;
388
389 if (argc > 1)
390 return 0;
391
392 ret = test_accept();
393 if (ret) {
394 fprintf(stderr, "test_accept failed\n");
395 return ret;
396 }
397 if (no_accept)
398 return 0;
399
400 ret = test_accept_sqpoll();
401 if (ret) {
402 fprintf(stderr, "test_accept_sqpoll failed\n");
403 return ret;
404 }
405
406 ret = test_accept_cancel(0);
407 if (ret) {
408 fprintf(stderr, "test_accept_cancel nodelay failed\n");
409 return ret;
410 }
411
412 ret = test_accept_cancel(10000);
413 if (ret) {
414 fprintf(stderr, "test_accept_cancel delay failed\n");
415 return ret;
416 }
417
418 ret = test_accept_many(128, 0);
419 if (ret) {
420 fprintf(stderr, "test_accept_many failed\n");
421 return ret;
422 }
423
424 ret = test_accept_many(128, 100000);
425 if (ret) {
426 fprintf(stderr, "test_accept_many failed\n");
427 return ret;
428 }
429
430 ret = test_accept_pending_on_exit();
431 if (ret) {
432 fprintf(stderr, "test_accept_pending_on_exit failed\n");
433 return ret;
434 }
435
436 return 0;
437 }
438