• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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