• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "helpers.h"
17 #include "liburing.h"
18 
19 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
20 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
21 
22 static int recv_thread_ready = 0;
23 static int recv_thread_done = 0;
24 
signal_var(int * var)25 static void signal_var(int *var)
26 {
27         pthread_mutex_lock(&mutex);
28         *var = 1;
29         pthread_cond_signal(&cond);
30         pthread_mutex_unlock(&mutex);
31 }
32 
wait_for_var(int * var)33 static void wait_for_var(int *var)
34 {
35         pthread_mutex_lock(&mutex);
36 
37         while (!*var)
38                 pthread_cond_wait(&cond, &mutex);
39 
40         pthread_mutex_unlock(&mutex);
41 }
42 
43 struct data {
44 	unsigned expected[2];
45 	unsigned is_mask[2];
46 	unsigned long timeout;
47 	unsigned short port;
48 	unsigned int addr;
49 	int stop;
50 };
51 
send_thread(void * arg)52 static void *send_thread(void *arg)
53 {
54 	struct sockaddr_in addr;
55 	struct data *data = arg;
56 	int s0;
57 
58 	wait_for_var(&recv_thread_ready);
59 
60 	s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
61 	assert(s0 != -1);
62 
63 	addr.sin_family = AF_INET;
64 	addr.sin_port = data->port;
65 	addr.sin_addr.s_addr = data->addr;
66 
67 	if (connect(s0, (struct sockaddr*)&addr, sizeof(addr)) != -1)
68 		wait_for_var(&recv_thread_done);
69 
70 	close(s0);
71 	return 0;
72 }
73 
recv_thread(void * arg)74 static void *recv_thread(void *arg)
75 {
76 	struct sockaddr_in addr = { };
77 	struct data *data = arg;
78 	struct io_uring_sqe *sqe;
79 	struct io_uring ring;
80 	int i, ret;
81 
82 	ret = io_uring_queue_init(8, &ring, 0);
83 	assert(ret == 0);
84 
85 	int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
86 	assert(s0 != -1);
87 
88 	int32_t val = 1;
89 	ret = setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
90 	assert(ret != -1);
91 	ret = setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
92 	assert(ret != -1);
93 
94 	addr.sin_family = AF_INET;
95 	data->addr = inet_addr("127.0.0.1");
96 	addr.sin_addr.s_addr = data->addr;
97 
98 	if (t_bind_ephemeral_port(s0, &addr)) {
99 		perror("bind");
100 		data->stop = 1;
101 		signal_var(&recv_thread_ready);
102 		goto err;
103 	}
104 	data->port = addr.sin_port;
105 
106 	ret = listen(s0, 128);
107 	assert(ret != -1);
108 
109 	signal_var(&recv_thread_ready);
110 
111 	sqe = io_uring_get_sqe(&ring);
112 	assert(sqe != NULL);
113 
114 	io_uring_prep_poll_add(sqe, s0, POLLIN | POLLHUP | POLLERR);
115 	sqe->flags |= IOSQE_IO_LINK;
116 	sqe->user_data = 1;
117 
118 	sqe = io_uring_get_sqe(&ring);
119 	assert(sqe != NULL);
120 
121 	struct __kernel_timespec ts;
122 	ts.tv_sec = data->timeout / 1000000000;
123 	ts.tv_nsec = data->timeout % 1000000000;
124 	io_uring_prep_link_timeout(sqe, &ts, 0);
125 	sqe->user_data = 2;
126 
127 	ret = io_uring_submit(&ring);
128 	assert(ret == 2);
129 
130 	for (i = 0; i < 2; i++) {
131 		struct io_uring_cqe *cqe;
132 		int idx;
133 
134 		if (io_uring_wait_cqe(&ring, &cqe)) {
135 			fprintf(stderr, "wait cqe failed\n");
136 			goto err;
137 		}
138 		idx = cqe->user_data - 1;
139 		if (data->is_mask[idx] && !(data->expected[idx] & cqe->res)) {
140 			fprintf(stderr, "cqe %" PRIu64 " got %x, wanted mask %x\n",
141 					(uint64_t) cqe->user_data, cqe->res,
142 					data->expected[idx]);
143 			goto err;
144 		} else if (!data->is_mask[idx] && cqe->res != data->expected[idx]) {
145 			fprintf(stderr, "cqe %" PRIu64 " got %d, wanted %d\n",
146 					(uint64_t) cqe->user_data, cqe->res,
147 					data->expected[idx]);
148 			goto err;
149 		}
150 		io_uring_cqe_seen(&ring, cqe);
151 	}
152 
153 	signal_var(&recv_thread_done);
154 	close(s0);
155 	io_uring_queue_exit(&ring);
156 	return NULL;
157 err:
158 	signal_var(&recv_thread_done);
159 	close(s0);
160 	io_uring_queue_exit(&ring);
161 	return (void *) 1;
162 }
163 
test_poll_timeout(int do_connect,unsigned long timeout)164 static int test_poll_timeout(int do_connect, unsigned long timeout)
165 {
166 	pthread_t t1, t2;
167 	struct data d;
168 	void *tret;
169 	int ret = 0;
170 
171 	recv_thread_ready = 0;
172 	recv_thread_done = 0;
173 
174 	memset(&d, 0, sizeof(d));
175 	d.timeout = timeout;
176 	if (!do_connect) {
177 		d.expected[0] = -ECANCELED;
178 		d.expected[1] = -ETIME;
179 	} else {
180 		d.expected[0] = POLLIN;
181 		d.is_mask[0] = 1;
182 		d.expected[1] = -ECANCELED;
183 	}
184 
185 	pthread_create(&t1, NULL, recv_thread, &d);
186 
187 	if (do_connect)
188 		pthread_create(&t2, NULL, send_thread, &d);
189 
190 	pthread_join(t1, &tret);
191 	if (tret)
192 		ret++;
193 
194 	if (do_connect) {
195 		pthread_join(t2, &tret);
196 		if (tret)
197 			ret++;
198 	}
199 
200 	return ret;
201 }
202 
main(int argc,char * argv[])203 int main(int argc, char *argv[])
204 {
205 	if (argc > 1)
206 		return 0;
207 
208 	srand(getpid());
209 
210 	if (test_poll_timeout(0, 200000000)) {
211 		fprintf(stderr, "poll timeout 0 failed\n");
212 		return 1;
213 	}
214 
215 	if (test_poll_timeout(1, 1000000000)) {
216 		fprintf(stderr, "poll timeout 1 failed\n");
217 		return 1;
218 	}
219 
220 	return 0;
221 }
222