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