1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Check that kernels that support it will return IORING_CQE_F_SOCK_NONEMPTY
4 * on accepts requests where more connections are pending.
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 #include <arpa/inet.h>
21 #include <pthread.h>
22
23 #include "liburing.h"
24 #include "helpers.h"
25
26 static int no_more_accept;
27
28 #define MAX_ACCEPTS 8
29
30 struct data {
31 pthread_t thread;
32 pthread_barrier_t barrier;
33 pthread_barrier_t conn_barrier;
34 int connects;
35 };
36
start_accept_listen(int port_off,int extra_flags)37 static int start_accept_listen(int port_off, int extra_flags)
38 {
39 struct sockaddr_in addr;
40 int32_t val = 1;
41 int fd, ret;
42
43 fd = socket(AF_INET, SOCK_STREAM | extra_flags, IPPROTO_TCP);
44
45 ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
46 assert(ret != -1);
47 ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
48 assert(ret != -1);
49
50 addr.sin_family = AF_INET;
51 addr.sin_port = htons(0x1235 + port_off);
52 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
53
54 ret = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
55 assert(ret != -1);
56 ret = listen(fd, 20000);
57 assert(ret != -1);
58
59 return fd;
60 }
61
connect_fn(void * data)62 static void *connect_fn(void *data)
63 {
64 struct sockaddr_in addr = { };
65 struct data *d = data;
66 int i;
67
68 pthread_barrier_wait(&d->barrier);
69
70 addr.sin_family = AF_INET;
71 addr.sin_port = htons(0x1235);
72 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
73
74 for (i = 0; i < d->connects; i++) {
75 int s;
76
77 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
78 if (s < 0) {
79 perror("socket");
80 break;
81 }
82 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
83 perror("connect");
84 break;
85 }
86 }
87
88 if (i)
89 pthread_barrier_wait(&d->conn_barrier);
90
91 return NULL;
92 }
93
setup_thread(struct data * d,int nconns)94 static void setup_thread(struct data *d, int nconns)
95 {
96 d->connects = nconns;
97 pthread_barrier_init(&d->barrier, NULL, 2);
98 pthread_barrier_init(&d->conn_barrier, NULL, 2);
99 pthread_create(&d->thread, NULL, connect_fn, d);
100 }
101
test_maccept(struct data * d,int flags,int fixed)102 static int test_maccept(struct data *d, int flags, int fixed)
103 {
104 struct io_uring_params p = { };
105 struct io_uring ring;
106 struct io_uring_cqe *cqe;
107 struct io_uring_sqe *sqe;
108 int err = 0, fd, ret, i, *fds;
109
110 p.flags = flags;
111 ret = io_uring_queue_init_params(8, &ring, &p);
112 if (ret == -EINVAL) {
113 return T_EXIT_SKIP;
114 } else if (ret < 0) {
115 fprintf(stderr, "ring setup failure: %d\n", ret);
116 return T_EXIT_FAIL;
117 }
118
119 if (!(p.features & IORING_FEAT_RECVSEND_BUNDLE)) {
120 no_more_accept = 1;
121 return 0;
122 }
123
124 setup_thread(d, MAX_ACCEPTS);
125
126 fds = malloc(MAX_ACCEPTS * sizeof(int));
127 memset(fds, -1, MAX_ACCEPTS * sizeof(int));
128
129 if (fixed) {
130 io_uring_register_ring_fd(&ring);
131
132 ret = io_uring_register_files(&ring, fds, MAX_ACCEPTS);
133 if (ret) {
134 fprintf(stderr, "file reg %d\n", ret);
135 return -1;
136 }
137 }
138
139 fd = start_accept_listen(0, 0);
140
141 pthread_barrier_wait(&d->barrier);
142
143 if (d->connects > 1)
144 pthread_barrier_wait(&d->conn_barrier);
145
146 for (i = 0; i < d->connects; i++) {
147 sqe = io_uring_get_sqe(&ring);
148 if (fixed)
149 io_uring_prep_accept_direct(sqe, fd, NULL, NULL, 0, i);
150 else
151 io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
152
153 ret = io_uring_submit_and_wait(&ring, 1);
154 assert(ret != -1);
155
156 ret = io_uring_wait_cqe(&ring, &cqe);
157 assert(!ret);
158 if (cqe->res < 0) {
159 fprintf(stderr, "res=%d\n", cqe->res);
160 break;
161 }
162 fds[i] = cqe->res;
163 if (d->connects == 1) {
164 if (cqe->flags & IORING_CQE_F_SOCK_NONEMPTY) {
165 fprintf(stderr, "Non-empty sock on single?\n");
166 err = 1;
167 break;
168 }
169 } else {
170 int last = i + 1 == d->connects;
171
172 if (last && cqe->flags & IORING_CQE_F_SOCK_NONEMPTY) {
173 fprintf(stderr, "Non-empty sock on last?\n");
174 err = 1;
175 break;
176 } else if (!last && !(cqe->flags & IORING_CQE_F_SOCK_NONEMPTY)) {
177 fprintf(stderr, "Empty on multi connect?\n");
178 err = 1;
179 break;
180 }
181 }
182 io_uring_cqe_seen(&ring, cqe);
183 }
184
185 close(fd);
186 if (!fixed) {
187 for (i = 0; i < MAX_ACCEPTS; i++)
188 if (fds[i] != -1)
189 close(fds[i]);
190 }
191 free(fds);
192 io_uring_queue_exit(&ring);
193 return err;
194 }
195
test(int flags,int fixed)196 static int test(int flags, int fixed)
197 {
198 struct data d;
199 void *tret;
200 int ret;
201
202 ret = test_maccept(&d, flags, fixed);
203 if (ret) {
204 fprintf(stderr, "test conns=1 failed\n");
205 return ret;
206 }
207 if (no_more_accept)
208 return T_EXIT_SKIP;
209
210 pthread_join(d.thread, &tret);
211
212 ret = test_maccept(&d, flags, fixed);
213 if (ret) {
214 fprintf(stderr, "test conns=MAX failed\n");
215 return ret;
216 }
217
218 pthread_join(d.thread, &tret);
219 return 0;
220 }
221
main(int argc,char * argv[])222 int main(int argc, char *argv[])
223 {
224 int ret;
225
226 if (argc > 1)
227 return T_EXIT_SKIP;
228
229 ret = test(0, 0);
230 if (no_more_accept)
231 return T_EXIT_SKIP;
232 if (ret) {
233 fprintf(stderr, "test 0 0 failed\n");
234 return ret;
235 }
236
237 ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN, 0);
238 if (ret) {
239 fprintf(stderr, "test DEFER 0 failed\n");
240 return ret;
241 }
242
243 ret = test(0, 1);
244 if (ret) {
245 fprintf(stderr, "test 0 1 failed\n");
246 return ret;
247 }
248
249 ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN, 1);
250 if (ret) {
251 fprintf(stderr, "test DEFER 1 failed\n");
252 return ret;
253 }
254
255 return 0;
256 }
257