xref: /aosp_15_r20/external/liburing/test/accept-link.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <assert.h>
9 #include <pthread.h>
10 #include <sys/socket.h>
11 #include <netinet/tcp.h>
12 #include <netinet/in.h>
13 #include <poll.h>
14 #include <arpa/inet.h>
15 
16 #include "liburing.h"
17 
18 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
19 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
20 
21 static int recv_thread_ready = 0;
22 static int recv_thread_done = 0;
23 
signal_var(int * var)24 static void signal_var(int *var)
25 {
26         pthread_mutex_lock(&mutex);
27         *var = 1;
28         pthread_cond_signal(&cond);
29         pthread_mutex_unlock(&mutex);
30 }
31 
wait_for_var(int * var)32 static void wait_for_var(int *var)
33 {
34         pthread_mutex_lock(&mutex);
35 
36         while (!*var)
37                 pthread_cond_wait(&cond, &mutex);
38 
39         pthread_mutex_unlock(&mutex);
40 }
41 
42 struct data {
43 	unsigned expected[2];
44 	unsigned just_positive[2];
45 	unsigned long timeout;
46 	unsigned short port;
47 	unsigned int addr;
48 	int stop;
49 };
50 
send_thread(void * arg)51 static void *send_thread(void *arg)
52 {
53 	struct data *data = arg;
54 	int ret;
55 
56 	wait_for_var(&recv_thread_ready);
57 
58 	if (data->stop)
59 		return NULL;
60 
61 	int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
62 	assert(s0 != -1);
63 
64 	struct sockaddr_in addr;
65 
66 	addr.sin_family = AF_INET;
67 	addr.sin_port = data->port;
68 	addr.sin_addr.s_addr = data->addr;
69 
70 	ret = connect(s0, (struct sockaddr*)&addr, sizeof(addr));
71 	assert(ret != -1);
72 
73 	wait_for_var(&recv_thread_done);
74 
75 	close(s0);
76 	return NULL;
77 }
78 
recv_thread(void * arg)79 void *recv_thread(void *arg)
80 {
81 	struct data *data = arg;
82 	struct io_uring ring;
83 	int i, ret;
84 
85 	ret = io_uring_queue_init(8, &ring, 0);
86 	assert(ret == 0);
87 
88 	int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
89 	assert(s0 != -1);
90 
91 	int32_t val = 1;
92 	ret = setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
93 	assert(ret != -1);
94 	ret = setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
95 	assert(ret != -1);
96 
97 	struct sockaddr_in addr;
98 
99 	addr.sin_family = AF_INET;
100 	data->addr = inet_addr("127.0.0.1");
101 	addr.sin_addr.s_addr = data->addr;
102 
103 	i = 0;
104 	do {
105 		data->port = htons(1025 + (rand() % 64510));
106 		addr.sin_port = data->port;
107 
108 		if (bind(s0, (struct sockaddr*)&addr, sizeof(addr)) != -1)
109 			break;
110 	} while (++i < 100);
111 
112 	if (i >= 100) {
113 		printf("Can't find good port, skipped\n");
114 		data->stop = 1;
115 		signal_var(&recv_thread_ready);
116 		goto out;
117 	}
118 
119 	ret = listen(s0, 128);
120 	assert(ret != -1);
121 
122 	signal_var(&recv_thread_ready);
123 
124 	struct io_uring_sqe *sqe;
125 
126 	sqe = io_uring_get_sqe(&ring);
127 	assert(sqe != NULL);
128 
129 	io_uring_prep_accept(sqe, s0, NULL, NULL, 0);
130 	sqe->flags |= IOSQE_IO_LINK;
131 	sqe->user_data = 1;
132 
133 	sqe = io_uring_get_sqe(&ring);
134 	assert(sqe != NULL);
135 
136 	struct __kernel_timespec ts;
137 	ts.tv_sec = data->timeout / 1000000000;
138 	ts.tv_nsec = data->timeout % 1000000000;
139 	io_uring_prep_link_timeout(sqe, &ts, 0);
140 	sqe->user_data = 2;
141 
142 	ret = io_uring_submit(&ring);
143 	assert(ret == 2);
144 
145 	for (i = 0; i < 2; i++) {
146 		struct io_uring_cqe *cqe;
147 		int idx;
148 
149 		if (io_uring_wait_cqe(&ring, &cqe)) {
150 			fprintf(stderr, "wait cqe failed\n");
151 			goto err;
152 		}
153 		idx = cqe->user_data - 1;
154 		if (cqe->res != data->expected[idx]) {
155 			if (cqe->res > 0 && data->just_positive[idx])
156 				goto ok;
157 			if (cqe->res == -EBADF) {
158 				fprintf(stdout, "Accept not supported, skipping\n");
159 				data->stop = 1;
160 				goto out;
161 			}
162 			fprintf(stderr, "cqe %" PRIu64 " got %d, wanted %d\n",
163 					(uint64_t) cqe->user_data, cqe->res,
164 					data->expected[idx]);
165 			goto err;
166 		}
167 ok:
168 		if (cqe->user_data == 1 && cqe->res > 0)
169 			close(cqe->res);
170 
171 		io_uring_cqe_seen(&ring, cqe);
172 	}
173 
174 	signal_var(&recv_thread_done);
175 
176 out:
177 	close(s0);
178 	return NULL;
179 err:
180 	close(s0);
181 	return (void *) 1;
182 }
183 
test_accept_timeout(int do_connect,unsigned long timeout)184 static int test_accept_timeout(int do_connect, unsigned long timeout)
185 {
186 	struct io_uring ring;
187 	struct io_uring_params p = {};
188 	pthread_t t1, t2;
189 	struct data d;
190 	void *tret;
191 	int ret, fast_poll;
192 
193 	ret = io_uring_queue_init_params(1, &ring, &p);
194 	if (ret) {
195 		fprintf(stderr, "queue_init: %d\n", ret);
196 		return 1;
197 	};
198 
199 	fast_poll = (p.features & IORING_FEAT_FAST_POLL) != 0;
200 	io_uring_queue_exit(&ring);
201 
202 	recv_thread_ready = 0;
203 	recv_thread_done = 0;
204 
205 	memset(&d, 0, sizeof(d));
206 	d.timeout = timeout;
207 	if (!do_connect) {
208 		if (fast_poll) {
209 			d.expected[0] = -ECANCELED;
210 			d.expected[1] = -ETIME;
211 		} else {
212 			d.expected[0] = -EINTR;
213 			d.expected[1] = -EALREADY;
214 		}
215 	} else {
216 		d.expected[0] = -1U;
217 		d.just_positive[0] = 1;
218 		d.expected[1] = -ECANCELED;
219 	}
220 
221 	pthread_create(&t1, NULL, recv_thread, &d);
222 
223 	if (do_connect)
224 		pthread_create(&t2, NULL, send_thread, &d);
225 
226 	pthread_join(t1, &tret);
227 	if (tret)
228 		ret++;
229 
230 	if (do_connect) {
231 		pthread_join(t2, &tret);
232 		if (tret)
233 			ret++;
234 	}
235 
236 	return ret;
237 }
238 
main(int argc,char * argv[])239 int main(int argc, char *argv[])
240 {
241 	if (argc > 1)
242 		return 0;
243 	if (test_accept_timeout(0, 200000000)) {
244 		fprintf(stderr, "accept timeout 0 failed\n");
245 		return 1;
246 	}
247 
248 	if (test_accept_timeout(1, 1000000000)) {
249 		fprintf(stderr, "accept and connect timeout 0 failed\n");
250 		return 1;
251 	}
252 
253 	return 0;
254 }
255