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
test_maccept(struct data * d,int flags,int fixed)62 static int test_maccept(struct data *d, int flags, int fixed)
63 {
64 struct io_uring_params p = { };
65 struct io_uring ring;
66 struct io_uring_cqe *cqe;
67 struct io_uring_sqe *sqe;
68 int err = 0, fd, ret, i, *fds;
69
70 p.flags = flags;
71 ret = io_uring_queue_init_params(8, &ring, &p);
72 if (ret == -EINVAL) {
73 return T_EXIT_SKIP;
74 } else if (ret < 0) {
75 fprintf(stderr, "ring setup failure: %d\n", ret);
76 return T_EXIT_FAIL;
77 }
78
79 if (!(p.features & IORING_FEAT_RECVSEND_BUNDLE)) {
80 no_more_accept = 1;
81 return 0;
82 }
83
84 fds = malloc(MAX_ACCEPTS * sizeof(int));
85 memset(fds, -1, MAX_ACCEPTS * sizeof(int));
86
87 if (fixed) {
88 io_uring_register_ring_fd(&ring);
89
90 ret = io_uring_register_files(&ring, fds, MAX_ACCEPTS);
91 if (ret) {
92 fprintf(stderr, "file reg %d\n", ret);
93 return -1;
94 }
95 }
96
97 fd = start_accept_listen(0, 0);
98
99 pthread_barrier_wait(&d->barrier);
100
101 if (d->connects > 1)
102 pthread_barrier_wait(&d->conn_barrier);
103
104 for (i = 0; i < d->connects; i++) {
105 sqe = io_uring_get_sqe(&ring);
106 if (fixed)
107 io_uring_prep_accept_direct(sqe, fd, NULL, NULL, 0, i);
108 else
109 io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
110
111 ret = io_uring_submit_and_wait(&ring, 1);
112 assert(ret != -1);
113
114 ret = io_uring_wait_cqe(&ring, &cqe);
115 assert(!ret);
116 if (cqe->res < 0) {
117 fprintf(stderr, "res=%d\n", cqe->res);
118 break;
119 }
120 fds[i] = cqe->res;
121 if (d->connects == 1) {
122 if (cqe->flags & IORING_CQE_F_SOCK_NONEMPTY) {
123 fprintf(stderr, "Non-empty sock on single?\n");
124 err = 1;
125 break;
126 }
127 } else {
128 int last = i + 1 == d->connects;
129
130 if (last && cqe->flags & IORING_CQE_F_SOCK_NONEMPTY) {
131 fprintf(stderr, "Non-empty sock on last?\n");
132 err = 1;
133 break;
134 } else if (!last && !(cqe->flags & IORING_CQE_F_SOCK_NONEMPTY)) {
135 fprintf(stderr, "Empty on multi connect?\n");
136 err = 1;
137 break;
138 }
139 }
140 io_uring_cqe_seen(&ring, cqe);
141 }
142
143 close(fd);
144 if (!fixed) {
145 for (i = 0; i < MAX_ACCEPTS; i++)
146 if (fds[i] != -1)
147 close(fds[i]);
148 }
149 free(fds);
150 io_uring_queue_exit(&ring);
151 return err;
152 }
153
connect_fn(void * data)154 static void *connect_fn(void *data)
155 {
156 struct sockaddr_in addr = { };
157 struct data *d = data;
158 int i;
159
160 pthread_barrier_wait(&d->barrier);
161
162 addr.sin_family = AF_INET;
163 addr.sin_port = htons(0x1235);
164 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
165
166 for (i = 0; i < d->connects; i++) {
167 int s;
168
169 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
170 if (s < 0) {
171 perror("socket");
172 break;
173 }
174 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
175 perror("connect");
176 break;
177 }
178 }
179
180 if (d->connects > 1)
181 pthread_barrier_wait(&d->conn_barrier);
182
183 return NULL;
184 }
185
setup_thread(struct data * d,int nconns)186 static void setup_thread(struct data *d, int nconns)
187 {
188 d->connects = nconns;
189 pthread_barrier_init(&d->barrier, NULL, 2);
190 pthread_barrier_init(&d->conn_barrier, NULL, 2);
191 pthread_create(&d->thread, NULL, connect_fn, d);
192 }
193
test(int flags,int fixed)194 static int test(int flags, int fixed)
195 {
196 struct data d;
197 void *tret;
198 int ret;
199
200 setup_thread(&d, 1);
201 ret = test_maccept(&d, flags, fixed);
202 if (ret) {
203 fprintf(stderr, "test conns=1 failed\n");
204 return ret;
205 }
206 if (no_more_accept)
207 return T_EXIT_SKIP;
208
209 pthread_join(d.thread, &tret);
210
211 setup_thread(&d, MAX_ACCEPTS);
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