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