xref: /aosp_15_r20/external/liburing/test/send_recv.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Simple test case showing using send and recv through io_uring
4  */
5 #include <errno.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <arpa/inet.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <pthread.h>
14 
15 #include "liburing.h"
16 #include "helpers.h"
17 
18 static char str[] = "This is a test of send and recv over io_uring!";
19 
20 #define MAX_MSG	128
21 
22 #define PORT	10202
23 #define HOST	"127.0.0.1"
24 
recv_prep(struct io_uring * ring,struct iovec * iov,int * sock,int registerfiles)25 static int recv_prep(struct io_uring *ring, struct iovec *iov, int *sock,
26 		     int registerfiles)
27 {
28 	struct sockaddr_in saddr;
29 	struct io_uring_sqe *sqe;
30 	int sockfd, ret, val, use_fd;
31 
32 	memset(&saddr, 0, sizeof(saddr));
33 	saddr.sin_family = AF_INET;
34 	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
35 	saddr.sin_port = htons(PORT);
36 
37 	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
38 	if (sockfd < 0) {
39 		perror("socket");
40 		return 1;
41 	}
42 
43 	val = 1;
44 	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
45 
46 	ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
47 	if (ret < 0) {
48 		perror("bind");
49 		goto err;
50 	}
51 
52 	if (registerfiles) {
53 		ret = io_uring_register_files(ring, &sockfd, 1);
54 		if (ret) {
55 			fprintf(stderr, "file reg failed\n");
56 			goto err;
57 		}
58 		use_fd = 0;
59 	} else {
60 		use_fd = sockfd;
61 	}
62 
63 	sqe = io_uring_get_sqe(ring);
64 	io_uring_prep_recv(sqe, use_fd, iov->iov_base, iov->iov_len, 0);
65 	if (registerfiles)
66 		sqe->flags |= IOSQE_FIXED_FILE;
67 	sqe->user_data = 2;
68 
69 	ret = io_uring_submit(ring);
70 	if (ret <= 0) {
71 		fprintf(stderr, "submit failed: %d\n", ret);
72 		goto err;
73 	}
74 
75 	*sock = sockfd;
76 	return 0;
77 err:
78 	close(sockfd);
79 	return 1;
80 }
81 
do_recv(struct io_uring * ring,struct iovec * iov)82 static int do_recv(struct io_uring *ring, struct iovec *iov)
83 {
84 	struct io_uring_cqe *cqe;
85 	int ret;
86 
87 	ret = io_uring_wait_cqe(ring, &cqe);
88 	if (ret) {
89 		fprintf(stdout, "wait_cqe: %d\n", ret);
90 		goto err;
91 	}
92 	if (cqe->res == -EINVAL) {
93 		fprintf(stdout, "recv not supported, skipping\n");
94 		return 0;
95 	}
96 	if (cqe->res < 0) {
97 		fprintf(stderr, "failed cqe: %d\n", cqe->res);
98 		goto err;
99 	}
100 
101 	if (cqe->res -1 != strlen(str)) {
102 		fprintf(stderr, "got wrong length: %d/%d\n", cqe->res,
103 							(int) strlen(str) + 1);
104 		goto err;
105 	}
106 
107 	if (strcmp(str, iov->iov_base)) {
108 		fprintf(stderr, "string mismatch\n");
109 		goto err;
110 	}
111 
112 	return 0;
113 err:
114 	return 1;
115 }
116 
117 struct recv_data {
118 	pthread_mutex_t mutex;
119 	int use_sqthread;
120 	int registerfiles;
121 };
122 
recv_fn(void * data)123 static void *recv_fn(void *data)
124 {
125 	struct recv_data *rd = data;
126 	char buf[MAX_MSG + 1];
127 	struct iovec iov = {
128 		.iov_base = buf,
129 		.iov_len = sizeof(buf) - 1,
130 	};
131 	struct io_uring_params p = { };
132 	struct io_uring ring;
133 	int ret, sock;
134 
135 	if (rd->use_sqthread)
136 		p.flags = IORING_SETUP_SQPOLL;
137 	ret = t_create_ring_params(1, &ring, &p);
138 	if (ret == T_SETUP_SKIP) {
139 		pthread_mutex_unlock(&rd->mutex);
140 		ret = 0;
141 		goto err;
142 	} else if (ret < 0) {
143 		pthread_mutex_unlock(&rd->mutex);
144 		goto err;
145 	}
146 
147 	if (rd->use_sqthread && !rd->registerfiles) {
148 		if (!(p.features & IORING_FEAT_SQPOLL_NONFIXED)) {
149 			fprintf(stdout, "Non-registered SQPOLL not available, skipping\n");
150 			pthread_mutex_unlock(&rd->mutex);
151 			goto err;
152 		}
153 	}
154 
155 	ret = recv_prep(&ring, &iov, &sock, rd->registerfiles);
156 	if (ret) {
157 		fprintf(stderr, "recv_prep failed: %d\n", ret);
158 		goto err;
159 	}
160 	pthread_mutex_unlock(&rd->mutex);
161 	ret = do_recv(&ring, &iov);
162 
163 	close(sock);
164 	io_uring_queue_exit(&ring);
165 err:
166 	return (void *)(intptr_t)ret;
167 }
168 
do_send(void)169 static int do_send(void)
170 {
171 	struct sockaddr_in saddr;
172 	struct iovec iov = {
173 		.iov_base = str,
174 		.iov_len = sizeof(str),
175 	};
176 	struct io_uring ring;
177 	struct io_uring_cqe *cqe;
178 	struct io_uring_sqe *sqe;
179 	int sockfd, ret;
180 
181 	ret = io_uring_queue_init(1, &ring, 0);
182 	if (ret) {
183 		fprintf(stderr, "queue init failed: %d\n", ret);
184 		return 1;
185 	}
186 
187 	memset(&saddr, 0, sizeof(saddr));
188 	saddr.sin_family = AF_INET;
189 	saddr.sin_port = htons(PORT);
190 	inet_pton(AF_INET, HOST, &saddr.sin_addr);
191 
192 	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
193 	if (sockfd < 0) {
194 		perror("socket");
195 		return 1;
196 	}
197 
198 	ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
199 	if (ret < 0) {
200 		perror("connect");
201 		return 1;
202 	}
203 
204 	sqe = io_uring_get_sqe(&ring);
205 	io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
206 	sqe->user_data = 1;
207 
208 	ret = io_uring_submit(&ring);
209 	if (ret <= 0) {
210 		fprintf(stderr, "submit failed: %d\n", ret);
211 		goto err;
212 	}
213 
214 	ret = io_uring_wait_cqe(&ring, &cqe);
215 	if (cqe->res == -EINVAL) {
216 		fprintf(stdout, "send not supported, skipping\n");
217 		close(sockfd);
218 		return 0;
219 	}
220 	if (cqe->res != iov.iov_len) {
221 		fprintf(stderr, "failed cqe: %d\n", cqe->res);
222 		goto err;
223 	}
224 
225 	close(sockfd);
226 	return 0;
227 err:
228 	close(sockfd);
229 	return 1;
230 }
231 
test(int use_sqthread,int regfiles)232 static int test(int use_sqthread, int regfiles)
233 {
234 	pthread_mutexattr_t attr;
235 	pthread_t recv_thread;
236 	struct recv_data rd;
237 	int ret;
238 	void *retval;
239 
240 	pthread_mutexattr_init(&attr);
241 	pthread_mutexattr_setpshared(&attr, 1);
242 	pthread_mutex_init(&rd.mutex, &attr);
243 	pthread_mutex_lock(&rd.mutex);
244 	rd.use_sqthread = use_sqthread;
245 	rd.registerfiles = regfiles;
246 
247 	ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
248 	if (ret) {
249 		fprintf(stderr, "Thread create failed: %d\n", ret);
250 		pthread_mutex_unlock(&rd.mutex);
251 		return 1;
252 	}
253 
254 	pthread_mutex_lock(&rd.mutex);
255 	do_send();
256 	pthread_join(recv_thread, &retval);
257 	return (intptr_t)retval;
258 }
259 
main(int argc,char * argv[])260 int main(int argc, char *argv[])
261 {
262 	int ret;
263 
264 	if (argc > 1)
265 		return 0;
266 
267 	ret = test(0, 0);
268 	if (ret) {
269 		fprintf(stderr, "test sqthread=0 failed\n");
270 		return ret;
271 	}
272 
273 	ret = test(1, 1);
274 	if (ret) {
275 		fprintf(stderr, "test sqthread=1 reg=1 failed\n");
276 		return ret;
277 	}
278 
279 	ret = test(1, 0);
280 	if (ret) {
281 		fprintf(stderr, "test sqthread=1 reg=0 failed\n");
282 		return ret;
283 	}
284 
285 	return 0;
286 }
287