• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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