xref: /spdk/examples/sock/hello_world/hello_sock.c (revision 88653b4fe04932b0f85ef2a78078a03f54c96ba3)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 #include "spdk/thread.h"
36 #include "spdk/env.h"
37 #include "spdk/event.h"
38 #include "spdk/log.h"
39 #include "spdk/string.h"
40 
41 #include "spdk/sock.h"
42 
43 #define ACCEPT_TIMEOUT_US 1000
44 #define CLOSE_TIMEOUT_US 1000000
45 #define BUFFER_SIZE 1024
46 #define ADDR_STR_LEN INET6_ADDRSTRLEN
47 
48 static bool g_is_running;
49 
50 static char *g_host;
51 static char *g_sock_impl_name;
52 static int g_port;
53 static bool g_is_server;
54 static int g_zcopy;
55 static bool g_verbose;
56 
57 /*
58  * We'll use this struct to gather housekeeping hello_context to pass between
59  * our events and callbacks.
60  */
61 struct hello_context_t {
62 	bool is_server;
63 	char *host;
64 	char *sock_impl_name;
65 	int port;
66 	int zcopy;
67 
68 	bool verbose;
69 	int bytes_in;
70 	int bytes_out;
71 
72 	struct spdk_sock *sock;
73 
74 	struct spdk_sock_group *group;
75 	struct spdk_poller *poller_in;
76 	struct spdk_poller *poller_out;
77 	struct spdk_poller *time_out;
78 
79 	int rc;
80 };
81 
82 /*
83  * Usage function for printing parameters that are specific to this application
84  */
85 static void
86 hello_sock_usage(void)
87 {
88 	printf(" -H host_addr  host address\n");
89 	printf(" -P port       port number\n");
90 	printf(" -N sock_impl  socket implementation, e.g., -N posix or -N uring\n");
91 	printf(" -S            start in server mode\n");
92 	printf(" -V            print out additional informations\n");
93 	printf(" -z            disable zero copy send for the given sock implementation\n");
94 	printf(" -Z            enable zero copy send for the given sock implementation\n");
95 }
96 
97 /*
98  * This function is called to parse the parameters that are specific to this application
99  */
100 static int hello_sock_parse_arg(int ch, char *arg)
101 {
102 	switch (ch) {
103 	case 'H':
104 		g_host = arg;
105 		break;
106 	case 'N':
107 		g_sock_impl_name = arg;
108 		break;
109 	case 'P':
110 		g_port = spdk_strtol(arg, 10);
111 		if (g_port < 0) {
112 			fprintf(stderr, "Invalid port ID\n");
113 			return g_port;
114 		}
115 		break;
116 	case 'S':
117 		g_is_server = 1;
118 		break;
119 	case 'V':
120 		g_verbose = true;
121 		break;
122 	case 'Z':
123 		g_zcopy = 1;
124 		break;
125 	case 'z':
126 		g_zcopy = 0;
127 		break;
128 	default:
129 		return -EINVAL;
130 	}
131 	return 0;
132 }
133 
134 static int
135 hello_sock_close_timeout_poll(void *arg)
136 {
137 	struct hello_context_t *ctx = arg;
138 	SPDK_NOTICELOG("Connection closed\n");
139 
140 	spdk_poller_unregister(&ctx->time_out);
141 	spdk_poller_unregister(&ctx->poller_in);
142 	spdk_sock_close(&ctx->sock);
143 	spdk_sock_group_close(&ctx->group);
144 
145 	spdk_app_stop(ctx->rc);
146 	return SPDK_POLLER_BUSY;
147 }
148 
149 static int
150 hello_sock_quit(struct hello_context_t *ctx, int rc)
151 {
152 	ctx->rc = rc;
153 	spdk_poller_unregister(&ctx->poller_out);
154 	if (!ctx->time_out) {
155 		ctx->time_out = SPDK_POLLER_REGISTER(hello_sock_close_timeout_poll, ctx,
156 						     CLOSE_TIMEOUT_US);
157 	}
158 	return 0;
159 }
160 
161 static int
162 hello_sock_recv_poll(void *arg)
163 {
164 	struct hello_context_t *ctx = arg;
165 	int rc;
166 	char buf_in[BUFFER_SIZE];
167 
168 	/*
169 	 * Get response
170 	 */
171 	rc = spdk_sock_recv(ctx->sock, buf_in, sizeof(buf_in) - 1);
172 
173 	if (rc <= 0) {
174 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
175 			return SPDK_POLLER_IDLE;
176 		}
177 
178 		SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
179 			    errno, spdk_strerror(errno));
180 		return SPDK_POLLER_BUSY;
181 	}
182 
183 	if (rc > 0) {
184 		ctx->bytes_in += rc;
185 		buf_in[rc] = '\0';
186 		printf("%s", buf_in);
187 	}
188 
189 	return SPDK_POLLER_BUSY;
190 }
191 
192 static int
193 hello_sock_writev_poll(void *arg)
194 {
195 	struct hello_context_t *ctx = arg;
196 	int rc = 0;
197 	char buf_out[BUFFER_SIZE];
198 	struct iovec iov;
199 	ssize_t n;
200 
201 	n = read(STDIN_FILENO, buf_out, sizeof(buf_out));
202 	if (n == 0 || !g_is_running) {
203 		/* EOF */
204 		SPDK_NOTICELOG("Closing connection...\n");
205 		hello_sock_quit(ctx, 0);
206 		return SPDK_POLLER_IDLE;
207 	}
208 	if (n > 0) {
209 		/*
210 		 * Send message to the server
211 		 */
212 		iov.iov_base = buf_out;
213 		iov.iov_len = n;
214 		rc = spdk_sock_writev(ctx->sock, &iov, 1);
215 		if (rc > 0) {
216 			ctx->bytes_out += rc;
217 		}
218 	}
219 	return rc > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
220 }
221 
222 static int
223 hello_sock_connect(struct hello_context_t *ctx)
224 {
225 	int rc;
226 	char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN];
227 	uint16_t cport, sport;
228 	struct spdk_sock_opts opts;
229 
230 	opts.opts_size = sizeof(opts);
231 	spdk_sock_get_default_opts(&opts);
232 	opts.zcopy = ctx->zcopy;
233 
234 	SPDK_NOTICELOG("Connecting to the server on %s:%d with sock_impl(%s)\n", ctx->host, ctx->port,
235 		       ctx->sock_impl_name);
236 
237 	ctx->sock = spdk_sock_connect_ext(ctx->host, ctx->port, ctx->sock_impl_name, &opts);
238 	if (ctx->sock == NULL) {
239 		SPDK_ERRLOG("connect error(%d): %s\n", errno, spdk_strerror(errno));
240 		return -1;
241 	}
242 
243 	rc = spdk_sock_getaddr(ctx->sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport);
244 	if (rc < 0) {
245 		SPDK_ERRLOG("Cannot get connection addresses\n");
246 		spdk_sock_close(&ctx->sock);
247 		return -1;
248 	}
249 
250 	SPDK_NOTICELOG("Connection accepted from (%s, %hu) to (%s, %hu)\n", caddr, cport, saddr, sport);
251 
252 	fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
253 
254 	g_is_running = true;
255 	ctx->poller_in = SPDK_POLLER_REGISTER(hello_sock_recv_poll, ctx, 0);
256 	ctx->poller_out = SPDK_POLLER_REGISTER(hello_sock_writev_poll, ctx, 0);
257 
258 	return 0;
259 }
260 
261 static void
262 hello_sock_cb(void *arg, struct spdk_sock_group *group, struct spdk_sock *sock)
263 {
264 	ssize_t n;
265 	char buf[BUFFER_SIZE];
266 	struct iovec iov;
267 	struct hello_context_t *ctx = arg;
268 
269 	n = spdk_sock_recv(sock, buf, sizeof(buf));
270 	if (n < 0) {
271 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
272 			SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
273 				    errno, spdk_strerror(errno));
274 			return;
275 		}
276 
277 		SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
278 			    errno, spdk_strerror(errno));
279 	}
280 
281 	if (n > 0) {
282 		ctx->bytes_in += n;
283 		iov.iov_base = buf;
284 		iov.iov_len = n;
285 		n = spdk_sock_writev(sock, &iov, 1);
286 		if (n > 0) {
287 			ctx->bytes_out += n;
288 		}
289 		return;
290 	}
291 
292 	/* Connection closed */
293 	SPDK_NOTICELOG("Connection closed\n");
294 	spdk_sock_group_remove_sock(group, sock);
295 	spdk_sock_close(&sock);
296 }
297 
298 static int
299 hello_sock_accept_poll(void *arg)
300 {
301 	struct hello_context_t *ctx = arg;
302 	struct spdk_sock *sock;
303 	int rc;
304 	int count = 0;
305 	char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN];
306 	uint16_t cport, sport;
307 
308 	if (!g_is_running) {
309 		hello_sock_quit(ctx, 0);
310 		return SPDK_POLLER_IDLE;
311 	}
312 
313 	while (1) {
314 		sock = spdk_sock_accept(ctx->sock);
315 		if (sock != NULL) {
316 			rc = spdk_sock_getaddr(sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport);
317 			if (rc < 0) {
318 				SPDK_ERRLOG("Cannot get connection addresses\n");
319 				spdk_sock_close(&ctx->sock);
320 				return SPDK_POLLER_IDLE;
321 			}
322 
323 			SPDK_NOTICELOG("Accepting a new connection from (%s, %hu) to (%s, %hu)\n",
324 				       caddr, cport, saddr, sport);
325 
326 			rc = spdk_sock_group_add_sock(ctx->group, sock,
327 						      hello_sock_cb, ctx);
328 
329 			if (rc < 0) {
330 				spdk_sock_close(&sock);
331 				SPDK_ERRLOG("failed\n");
332 				break;
333 			}
334 
335 			count++;
336 		} else {
337 			if (errno != EAGAIN && errno != EWOULDBLOCK) {
338 				SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno));
339 			}
340 			break;
341 		}
342 	}
343 
344 	return count > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
345 }
346 
347 static int
348 hello_sock_group_poll(void *arg)
349 {
350 	struct hello_context_t *ctx = arg;
351 	int rc;
352 
353 	rc = spdk_sock_group_poll(ctx->group);
354 	if (rc < 0) {
355 		SPDK_ERRLOG("Failed to poll sock_group=%p\n", ctx->group);
356 	}
357 
358 	return rc > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
359 }
360 
361 static int
362 hello_sock_listen(struct hello_context_t *ctx)
363 {
364 	struct spdk_sock_opts opts;
365 
366 	opts.opts_size = sizeof(opts);
367 	spdk_sock_get_default_opts(&opts);
368 	opts.zcopy = ctx->zcopy;
369 
370 	ctx->sock = spdk_sock_listen_ext(ctx->host, ctx->port, ctx->sock_impl_name, &opts);
371 	if (ctx->sock == NULL) {
372 		SPDK_ERRLOG("Cannot create server socket\n");
373 		return -1;
374 	}
375 
376 	SPDK_NOTICELOG("Listening connection on %s:%d with sock_impl(%s)\n", ctx->host, ctx->port,
377 		       ctx->sock_impl_name);
378 
379 	/*
380 	 * Create sock group for server socket
381 	 */
382 	ctx->group = spdk_sock_group_create(NULL);
383 
384 	g_is_running = true;
385 
386 	/*
387 	 * Start acceptor and group poller
388 	 */
389 	ctx->poller_in = SPDK_POLLER_REGISTER(hello_sock_accept_poll, ctx,
390 					      ACCEPT_TIMEOUT_US);
391 	ctx->poller_out = SPDK_POLLER_REGISTER(hello_sock_group_poll, ctx, 0);
392 
393 	return 0;
394 }
395 
396 static void
397 hello_sock_shutdown_cb(void)
398 {
399 	g_is_running = false;
400 }
401 
402 /*
403  * Our initial event that kicks off everything from main().
404  */
405 static void
406 hello_start(void *arg1)
407 {
408 	struct hello_context_t *ctx = arg1;
409 	int rc;
410 
411 	SPDK_NOTICELOG("Successfully started the application\n");
412 
413 	if (ctx->is_server) {
414 		rc = hello_sock_listen(ctx);
415 	} else {
416 		rc = hello_sock_connect(ctx);
417 	}
418 
419 	if (rc) {
420 		spdk_app_stop(-1);
421 		return;
422 	}
423 }
424 
425 int
426 main(int argc, char **argv)
427 {
428 	struct spdk_app_opts opts = {};
429 	int rc = 0;
430 	struct hello_context_t hello_context = {};
431 
432 	/* Set default values in opts structure. */
433 	spdk_app_opts_init(&opts, sizeof(opts));
434 	opts.name = "hello_sock";
435 	opts.shutdown_cb = hello_sock_shutdown_cb;
436 
437 	if ((rc = spdk_app_parse_args(argc, argv, &opts, "H:N:P:SVzZ", NULL, hello_sock_parse_arg,
438 				      hello_sock_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) {
439 		exit(rc);
440 	}
441 	hello_context.is_server = g_is_server;
442 	hello_context.host = g_host;
443 	hello_context.sock_impl_name = g_sock_impl_name;
444 	hello_context.port = g_port;
445 	hello_context.zcopy = g_zcopy;
446 	hello_context.verbose = g_verbose;
447 
448 	rc = spdk_app_start(&opts, hello_start, &hello_context);
449 	if (rc) {
450 		SPDK_ERRLOG("ERROR starting application\n");
451 	}
452 
453 	SPDK_NOTICELOG("Exiting from application\n");
454 
455 	if (hello_context.verbose) {
456 		printf("** %d bytes received, %d bytes sent **\n",
457 		       hello_context.bytes_in, hello_context.bytes_out);
458 	}
459 
460 	/* Gracefully close out all of the SPDK subsystems. */
461 	spdk_app_fini();
462 	return rc;
463 }
464