diff --git a/examples/Makefile b/examples/Makefile index 8318cac900..a5fe76ca6d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -34,7 +34,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y += bdev blob ioat nvme +DIRS-y += bdev blob ioat nvme sock .PHONY: all clean $(DIRS-y) diff --git a/examples/sock/Makefile b/examples/sock/Makefile new file mode 100644 index 0000000000..097061fd12 --- /dev/null +++ b/examples/sock/Makefile @@ -0,0 +1,47 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +DIRS-y += hello_world + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) + @: + +clean: $(DIRS-y) + @: + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/examples/sock/hello_world/.gitignore b/examples/sock/hello_world/.gitignore new file mode 100644 index 0000000000..95ffb143c1 --- /dev/null +++ b/examples/sock/hello_world/.gitignore @@ -0,0 +1 @@ +hello_sock diff --git a/examples/sock/hello_world/Makefile b/examples/sock/hello_world/Makefile new file mode 100644 index 0000000000..f638153cfb --- /dev/null +++ b/examples/sock/hello_world/Makefile @@ -0,0 +1,54 @@ +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk +include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk + +APP = hello_sock + +C_SRCS := hello_sock.c + +SPDK_LIB_LIST += event thread util conf trace log jsonrpc json rpc + +LIBS += $(SOCK_MODULES_LINKER_ARGS) $(SPDK_LIB_LINKER_ARGS) $(ENV_LINKER_ARGS) + +all : $(APP) + @: + +$(APP) : $(OBJS) $(SPDK_LIB_FILES) $(SOCK_MODULES_FILES) $(LINKER_MODULES) $(ENV_LIBS) + $(LINK_C) + +clean : + $(CLEAN_C) $(APP) + +include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/examples/sock/hello_world/hello_sock.c b/examples/sock/hello_world/hello_sock.c new file mode 100644 index 0000000000..e6025df776 --- /dev/null +++ b/examples/sock/hello_world/hello_sock.c @@ -0,0 +1,421 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" +#include "spdk/thread.h" +#include "spdk/env.h" +#include "spdk/event.h" +#include "spdk/log.h" +#include "spdk/string.h" + +#include "spdk/sock.h" +#include "spdk/net.h" + +#define ACCEPT_TIMEOUT_US 1000 +#define CLOSE_TIMEOUT_US 1000000 +#define BUFFER_SIZE 1024 +#define ADDR_STR_LEN INET6_ADDRSTRLEN + +static bool g_is_running; + +static char *g_host; +static int g_port; +static bool g_is_server; +static bool g_verbose; + +/* + * We'll use this struct to gather housekeeping hello_context to pass between + * our events and callbacks. + */ +struct hello_context_t { + bool is_server; + char *host; + int port; + + bool verbose; + int bytes_in; + int bytes_out; + + struct spdk_sock *sock; + + struct spdk_sock_group *group; + struct spdk_poller *poller_in; + struct spdk_poller *poller_out; + struct spdk_poller *time_out; +}; + +/* + * Usage function for printing parameters that are specific to this application + */ +static void +hello_sock_usage(void) +{ + printf(" -H host_addr host address\n"); + printf(" -P port port number\n"); + printf(" -S start in server mode\n"); + printf(" -V print out additional informations"); +} + +/* + * This function is called to parse the parameters that are specific to this application + */ +static void hello_sock_parse_arg(int ch, char *arg) +{ + switch (ch) { + case 'H': + g_host = arg; + break; + case 'P': + g_port = atoi(arg); + break; + case 'S': + g_is_server = 1; + break; + case 'V': + g_verbose = true; + } +} + +static int +hello_sock_close_timeout_poll(void *arg) +{ + struct hello_context_t *ctx = arg; + SPDK_NOTICELOG("Connection closed\n"); + + spdk_poller_unregister(&ctx->time_out); + spdk_poller_unregister(&ctx->poller_in); + spdk_sock_close(&ctx->sock); + + spdk_app_stop(0); + return 0; +} + +static int +hello_sock_recv_poll(void *arg) +{ + struct hello_context_t *ctx = arg; + int rc; + char buf_in[BUFFER_SIZE]; + + /* + * Get response + */ + rc = spdk_sock_recv(ctx->sock, buf_in, sizeof(buf_in) - 1); + + if (rc <= 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + + SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n", + errno, spdk_strerror(errno)); + return -1; + } + + if (rc > 0) { + ctx->bytes_in += rc; + buf_in[rc] = '\0'; + printf("%s", buf_in); + } + + return 0; +} + +static int +hello_sock_writev_poll(void *arg) +{ + struct hello_context_t *ctx = arg; + int rc = 0; + char buf_out[BUFFER_SIZE]; + struct iovec iov; + ssize_t n; + + n = read(STDIN_FILENO, buf_out, sizeof(buf_out)); + if (n == 0 || !g_is_running) { + /* EOF */ + SPDK_NOTICELOG("Closing connection...\n"); + + ctx->time_out = spdk_poller_register(hello_sock_close_timeout_poll, ctx, + CLOSE_TIMEOUT_US); + + spdk_poller_unregister(&ctx->poller_out); + return 0; + } + if (n > 0) { + /* + * Send message to the server + */ + iov.iov_base = buf_out; + iov.iov_len = n; + rc = spdk_sock_writev(ctx->sock, &iov, 1); + if (rc > 0) { + ctx->bytes_out += rc; + } + } + return rc; +} + +static int +hello_sock_connect(struct hello_context_t *ctx) +{ + int rc; + char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN]; + + SPDK_NOTICELOG("Connecting to the server on %s:%d\n", ctx->host, ctx->port); + + ctx->sock = spdk_sock_connect(ctx->host, ctx->port); + if (ctx->sock == NULL) { + SPDK_ERRLOG("connect error(%d): %s\n", errno, spdk_strerror(errno)); + return -1; + } + + rc = spdk_sock_getaddr(ctx->sock, saddr, sizeof(saddr), caddr, sizeof(caddr)); + if (rc < 0) { + SPDK_ERRLOG("Cannot get connection addresses\n"); + spdk_sock_close(&ctx->sock); + return -1; + } + + SPDK_NOTICELOG("Connection accepted from %s to %s\n", caddr, saddr); + + fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK); + + g_is_running = true; + ctx->poller_in = spdk_poller_register(hello_sock_recv_poll, ctx, 0); + ctx->poller_out = spdk_poller_register(hello_sock_writev_poll, ctx, 0); + + return 0; +} + +static void +hello_sock_cb(void *arg, struct spdk_sock_group *group, struct spdk_sock *sock) +{ + ssize_t n; + char buf[BUFFER_SIZE]; + struct iovec iov; + struct hello_context_t *ctx = arg; + + n = spdk_sock_recv(sock, buf, sizeof(buf)); + if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n", + errno, spdk_strerror(errno)); + return; + } + + SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n", + errno, spdk_strerror(errno)); + } + + if (n > 0) { + ctx->bytes_in += n; + iov.iov_base = buf; + iov.iov_len = n; + n = spdk_sock_writev(sock, &iov, 1); + if (n > 0) { + ctx->bytes_out += n; + } + return; + } + + /* Connection closed */ + SPDK_NOTICELOG("Connection closed\n"); + spdk_sock_group_remove_sock(group, sock); + spdk_sock_close(&sock); +} + +static int +hello_sock_accept_poll(void *arg) +{ + struct hello_context_t *ctx = arg; + struct spdk_sock *sock; + int rc; + int count = 0; + char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN]; + + if (!g_is_running) { + spdk_poller_unregister(&ctx->poller_in); + spdk_poller_unregister(&ctx->poller_out); + spdk_sock_close(&ctx->sock); + spdk_sock_group_close(&ctx->group); + spdk_app_stop(0); + return 0; + } + + while (1) { + sock = spdk_sock_accept(ctx->sock); + if (sock != NULL) { + + spdk_sock_getaddr(sock, saddr, sizeof(saddr), caddr, sizeof(caddr)); + + SPDK_NOTICELOG("Accepting a new connection from %s to %s\n", + caddr, saddr); + + rc = spdk_sock_group_add_sock(ctx->group, sock, + hello_sock_cb, ctx); + + if (rc < 0) { + spdk_sock_close(&sock); + SPDK_ERRLOG("failed\n"); + break; + } + + count++; + } else { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno)); + } + break; + } + } + + return count; +} + +static int +hello_sock_group_poll(void *arg) +{ + struct hello_context_t *ctx = arg; + int rc; + + rc = spdk_sock_group_poll(ctx->group); + if (rc < 0) { + SPDK_ERRLOG("Failed to poll sock_group=%p\n", ctx->group); + } + + return -1; +} + +static int +hello_sock_listen(struct hello_context_t *ctx) +{ + ctx->sock = spdk_sock_listen(ctx->host, ctx->port); + if (ctx->sock == NULL) { + SPDK_ERRLOG("Cannot create server socket\n"); + return -1; + } + + SPDK_NOTICELOG("Listening connection on %s:%d\n", ctx->host, ctx->port); + + /* + * Create sock group for server socket + */ + ctx->group = spdk_sock_group_create(); + + g_is_running = true; + + /* + * Start acceptor and group poller + */ + ctx->poller_in = spdk_poller_register(hello_sock_accept_poll, ctx, + ACCEPT_TIMEOUT_US); + ctx->poller_out = spdk_poller_register(hello_sock_group_poll, ctx, 0); + + return 0; +} + +static void +hello_sock_shutdown_cb(void) +{ + g_is_running = false; +} +/* + * Our initial event that kicks off everything from main(). + */ +static void +hello_start(void *arg1, void *arg2) +{ + struct hello_context_t *ctx = arg1; + int rc = 0; + + SPDK_NOTICELOG("Successfully started the application\n"); + + if (ctx->is_server) { + rc = hello_sock_listen(ctx); + } else { + rc = hello_sock_connect(ctx); + } + + if (rc) { + spdk_app_stop(-1); + return; + } +} + +int +main(int argc, char **argv) +{ + struct spdk_app_opts opts = {}; + int rc = 0; + struct hello_context_t hello_context = {}; + + /* Set default values in opts structure. */ + spdk_app_opts_init(&opts); + opts.name = "hello_sock"; + opts.config_file = "sock.conf"; + opts.shutdown_cb = hello_sock_shutdown_cb; + + if ((rc = spdk_app_parse_args(argc, argv, &opts, "H:P:SV", hello_sock_parse_arg, + hello_sock_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) { + exit(rc); + } + hello_context.is_server = g_is_server; + hello_context.host = g_host; + hello_context.port = g_port; + hello_context.verbose = g_verbose; + + rc = spdk_net_framework_start(); + if (rc) { + SPDK_ERRLOG("ERROR starting application\n"); + goto end; + } + + rc = spdk_app_start(&opts, hello_start, &hello_context, NULL); + if (rc) { + SPDK_ERRLOG("ERROR starting application\n"); + } + +end: + SPDK_NOTICELOG("Exiting from application\n"); + + if (hello_context.verbose) { + printf("** %d bytes received, %d bytes sent **\n", + hello_context.bytes_in, hello_context.bytes_out); + } + + spdk_net_framework_fini(); + + /* Gracefully close out all of the SPDK subsystems. */ + spdk_app_fini(); + return rc; +}