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