• 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 
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