diff options
Diffstat (limited to 'src/spdk/lib/event')
31 files changed, 6961 insertions, 0 deletions
diff --git a/src/spdk/lib/event/Makefile b/src/spdk/lib/event/Makefile new file mode 100644 index 00000000..659b85e9 --- /dev/null +++ b/src/spdk/lib/event/Makefile @@ -0,0 +1,42 @@ +# +# 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 + +LIBNAME = event +C_SRCS = app.c reactor.c rpc.c subsystem.c + +DIRS-y = rpc subsystems + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/app.c b/src/spdk/lib/event/app.c new file mode 100644 index 00000000..012e2920 --- /dev/null +++ b/src/spdk/lib/event/app.c @@ -0,0 +1,998 @@ +/*- + * 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_internal/event.h" + +#include "spdk/env.h" +#include "spdk/log.h" +#include "spdk/conf.h" +#include "spdk/thread.h" +#include "spdk/trace.h" +#include "spdk/string.h" +#include "spdk/rpc.h" +#include "spdk/util.h" + +#define SPDK_APP_DEFAULT_LOG_LEVEL SPDK_LOG_NOTICE +#define SPDK_APP_DEFAULT_LOG_PRINT_LEVEL SPDK_LOG_INFO +#define SPDK_APP_DEFAULT_BACKTRACE_LOG_LEVEL SPDK_LOG_ERROR + +#define SPDK_APP_DPDK_DEFAULT_MEM_SIZE -1 +#define SPDK_APP_DPDK_DEFAULT_MASTER_CORE -1 +#define SPDK_APP_DPDK_DEFAULT_MEM_CHANNEL -1 +#define SPDK_APP_DPDK_DEFAULT_CORE_MASK "0x1" + +struct spdk_app { + struct spdk_conf *config; + int shm_id; + spdk_app_shutdown_cb shutdown_cb; + int rc; +}; + +static struct spdk_app g_spdk_app; +static struct spdk_event *g_app_start_event = NULL; +static struct spdk_event *g_shutdown_event = NULL; +static int g_init_lcore; +static bool g_delay_subsystem_init = false; +static bool g_shutdown_sig_received = false; +static char *g_executable_name; +static struct spdk_app_opts g_default_opts; + +int +spdk_app_get_shm_id(void) +{ + return g_spdk_app.shm_id; +} + +/* append one empty option to indicate the end of the array */ +static const struct option g_cmdline_options[] = { +#define CONFIG_FILE_OPT_IDX 'c' + {"config", required_argument, NULL, CONFIG_FILE_OPT_IDX}, +#define LIMIT_COREDUMP_OPT_IDX 'd' + {"limit-coredump", no_argument, NULL, LIMIT_COREDUMP_OPT_IDX}, +#define TPOINT_GROUP_MASK_OPT_IDX 'e' + {"tpoint-group-mask", required_argument, NULL, TPOINT_GROUP_MASK_OPT_IDX}, +#define SINGLE_FILE_SEGMENTS_OPT_IDX 'g' + {"single-file-segments", no_argument, NULL, SINGLE_FILE_SEGMENTS_OPT_IDX}, +#define HELP_OPT_IDX 'h' + {"help", no_argument, NULL, HELP_OPT_IDX}, +#define SHM_ID_OPT_IDX 'i' + {"shm-id", required_argument, NULL, SHM_ID_OPT_IDX}, +#define CPUMASK_OPT_IDX 'm' + {"cpumask", required_argument, NULL, CPUMASK_OPT_IDX}, +#define MEM_CHANNELS_OPT_IDX 'n' + {"mem-channels", required_argument, NULL, MEM_CHANNELS_OPT_IDX}, +#define MASTER_CORE_OPT_IDX 'p' + {"master-core", required_argument, NULL, MASTER_CORE_OPT_IDX}, +#define RPC_SOCKET_OPT_IDX 'r' + {"rpc-socket", required_argument, NULL, RPC_SOCKET_OPT_IDX}, +#define MEM_SIZE_OPT_IDX 's' + {"mem-size", required_argument, NULL, MEM_SIZE_OPT_IDX}, +#define NO_PCI_OPT_IDX 'u' + {"no-pci", no_argument, NULL, NO_PCI_OPT_IDX}, +#define PCI_BLACKLIST_OPT_IDX 'B' + {"pci-blacklist", required_argument, NULL, PCI_BLACKLIST_OPT_IDX}, +#define TRACEFLAG_OPT_IDX 'L' + {"traceflag", required_argument, NULL, TRACEFLAG_OPT_IDX}, +#define HUGE_UNLINK_OPT_IDX 'R' + {"huge-unlink", no_argument, NULL, HUGE_UNLINK_OPT_IDX}, +#define PCI_WHITELIST_OPT_IDX 'W' + {"pci-whitelist", required_argument, NULL, PCI_WHITELIST_OPT_IDX}, +#define SILENCE_NOTICELOG_OPT_IDX 257 + {"silence-noticelog", no_argument, NULL, SILENCE_NOTICELOG_OPT_IDX}, +#define WAIT_FOR_RPC_OPT_IDX 258 + {"wait-for-rpc", no_argument, NULL, WAIT_FOR_RPC_OPT_IDX}, +}; + +/* Global section */ +#define GLOBAL_CONFIG_TMPL \ +"# Configuration file\n" \ +"#\n" \ +"# Please write all parameters using ASCII.\n" \ +"# The parameter must be quoted if it includes whitespace.\n" \ +"#\n" \ +"# Configuration syntax:\n" \ +"# Spaces at head of line are deleted, other spaces are as separator\n" \ +"# Lines starting with '#' are comments and not evaluated.\n" \ +"# Lines ending with '\\' are concatenated with the next line.\n" \ +"# Bracketed keys are section keys grouping the following value keys.\n" \ +"# Number of section key is used as a tag number.\n" \ +"# Ex. [TargetNode1] = TargetNode section key with tag number 1\n" \ +"[Global]\n" \ +" Comment \"Global section\"\n" \ +"\n" \ +" # Users can restrict work items to only run on certain cores by\n" \ +" # specifying a ReactorMask. Default is to allow work items to run\n" \ +" # on all cores. Core 0 must be set in the mask if one is specified.\n" \ +" # Default: 0xFFFF (cores 0-15)\n" \ +" ReactorMask \"0x%s\"\n" \ +"\n" \ +" # Tracepoint group mask for spdk trace buffers\n" \ +" # Default: 0x0 (all tracepoint groups disabled)\n" \ +" # Set to 0xFFFF to enable all tracepoint groups.\n" \ +" TpointGroupMask \"0x%" PRIX64 "\"\n" \ +"\n" \ + +static void +spdk_app_config_dump_global_section(FILE *fp) +{ + struct spdk_cpuset *coremask; + + if (NULL == fp) { + return; + } + + coremask = spdk_app_get_core_mask(); + + fprintf(fp, GLOBAL_CONFIG_TMPL, spdk_cpuset_fmt(coremask), + spdk_trace_get_tpoint_group_mask()); +} + +int +spdk_app_get_running_config(char **config_str, char *name) +{ + FILE *fp = NULL; + int fd = -1; + long length = 0, ret = 0; + char vbuf[BUFSIZ]; + char config_template[64]; + + snprintf(config_template, sizeof(config_template), "/tmp/%s.XXXXXX", name); + /* Create temporary file to hold config */ + fd = mkstemp(config_template); + if (fd == -1) { + SPDK_ERRLOG("mkstemp failed\n"); + return -1; + } + fp = fdopen(fd, "wb+"); + if (NULL == fp) { + SPDK_ERRLOG("error opening tmpfile fd = %d\n", fd); + return -1; + } + + /* Buffered IO */ + setvbuf(fp, vbuf, _IOFBF, BUFSIZ); + + spdk_app_config_dump_global_section(fp); + spdk_subsystem_config(fp); + + length = ftell(fp); + + *config_str = malloc(length + 1); + if (!*config_str) { + SPDK_ERRLOG("out-of-memory for config\n"); + fclose(fp); + return -1; + } + fseek(fp, 0, SEEK_SET); + ret = fread(*config_str, sizeof(char), length, fp); + if (ret < length) { + SPDK_ERRLOG("short read\n"); + } + fclose(fp); + (*config_str)[length] = '\0'; + + return 0; +} + +void +spdk_app_start_shutdown(void) +{ + if (g_shutdown_event != NULL) { + spdk_event_call(g_shutdown_event); + g_shutdown_event = NULL; + } else { + spdk_app_stop(0); + } +} + +static void +__shutdown_signal(int signo) +{ + if (!g_shutdown_sig_received) { + g_shutdown_sig_received = true; + spdk_app_start_shutdown(); + } +} + +static void +__shutdown_event_cb(void *arg1, void *arg2) +{ + g_spdk_app.shutdown_cb(); +} + +static int +spdk_app_opts_validate(const char *app_opts) +{ + int i = 0, j; + + for (i = 0; app_opts[i] != '\0'; i++) { + /* ignore getopt control characters */ + if (app_opts[i] == ':' || app_opts[i] == '+' || app_opts[i] == '-') { + continue; + } + + for (j = 0; SPDK_APP_GETOPT_STRING[j] != '\0'; j++) { + if (app_opts[i] == SPDK_APP_GETOPT_STRING[j]) { + return app_opts[i]; + } + } + } + return 0; +} + +void +spdk_app_opts_init(struct spdk_app_opts *opts) +{ + if (!opts) { + return; + } + + memset(opts, 0, sizeof(*opts)); + + opts->enable_coredump = true; + opts->shm_id = -1; + opts->mem_size = SPDK_APP_DPDK_DEFAULT_MEM_SIZE; + opts->master_core = SPDK_APP_DPDK_DEFAULT_MASTER_CORE; + opts->mem_channel = SPDK_APP_DPDK_DEFAULT_MEM_CHANNEL; + opts->reactor_mask = NULL; + opts->max_delay_us = 0; + opts->print_level = SPDK_APP_DEFAULT_LOG_PRINT_LEVEL; + opts->rpc_addr = SPDK_DEFAULT_RPC_ADDR; + opts->delay_subsystem_init = false; +} + +static int +spdk_app_setup_signal_handlers(struct spdk_app_opts *opts) +{ + struct sigaction sigact; + sigset_t sigmask; + int rc; + + /* Set up custom shutdown handling if the user requested it. */ + if (opts->shutdown_cb != NULL) { + g_shutdown_event = spdk_event_allocate(spdk_env_get_current_core(), + __shutdown_event_cb, + NULL, NULL); + } + + sigemptyset(&sigmask); + memset(&sigact, 0, sizeof(sigact)); + sigemptyset(&sigact.sa_mask); + + sigact.sa_handler = SIG_IGN; + rc = sigaction(SIGPIPE, &sigact, NULL); + if (rc < 0) { + SPDK_ERRLOG("sigaction(SIGPIPE) failed\n"); + return rc; + } + + /* Install the same handler for SIGINT and SIGTERM */ + sigact.sa_handler = __shutdown_signal; + + rc = sigaction(SIGINT, &sigact, NULL); + if (rc < 0) { + SPDK_ERRLOG("sigaction(SIGINT) failed\n"); + return rc; + } + sigaddset(&sigmask, SIGINT); + + rc = sigaction(SIGTERM, &sigact, NULL); + if (rc < 0) { + SPDK_ERRLOG("sigaction(SIGTERM) failed\n"); + return rc; + } + sigaddset(&sigmask, SIGTERM); + + if (opts->usr1_handler != NULL) { + sigact.sa_handler = opts->usr1_handler; + rc = sigaction(SIGUSR1, &sigact, NULL); + if (rc < 0) { + SPDK_ERRLOG("sigaction(SIGUSR1) failed\n"); + return rc; + } + sigaddset(&sigmask, SIGUSR1); + } + + pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL); + + return 0; +} + +static void +spdk_app_start_application(void) +{ + spdk_rpc_set_state(SPDK_RPC_RUNTIME); + spdk_event_call(g_app_start_event); +} + +static void +spdk_app_start_rpc(void *arg1, void *arg2) +{ + const char *rpc_addr = arg1; + + spdk_rpc_initialize(rpc_addr); + if (!g_delay_subsystem_init) { + spdk_app_start_application(); + } +} + +static struct spdk_conf * +spdk_app_setup_conf(const char *config_file) +{ + struct spdk_conf *config; + int rc; + + config = spdk_conf_allocate(); + assert(config != NULL); + if (config_file) { + rc = spdk_conf_read(config, config_file); + if (rc != 0) { + SPDK_ERRLOG("Could not read config file %s\n", config_file); + goto error; + } + if (spdk_conf_first_section(config) == NULL) { + SPDK_ERRLOG("Invalid config file %s\n", config_file); + goto error; + } + } + spdk_conf_set_as_default(config); + return config; + +error: + spdk_conf_free(config); + return NULL; +} + +static int +spdk_app_opts_add_pci_addr(struct spdk_app_opts *opts, struct spdk_pci_addr **list, char *bdf) +{ + struct spdk_pci_addr *tmp = *list; + size_t i = opts->num_pci_addr; + + tmp = realloc(tmp, sizeof(*tmp) * (i + 1)); + if (tmp == NULL) { + SPDK_ERRLOG("realloc error\n"); + return -ENOMEM; + } + + *list = tmp; + if (spdk_pci_addr_parse(*list + i, bdf) < 0) { + SPDK_ERRLOG("Invalid address %s\n", bdf); + return -EINVAL; + } + + opts->num_pci_addr++; + return 0; +} + +static int +spdk_app_read_config_file_global_params(struct spdk_app_opts *opts) +{ + struct spdk_conf_section *sp; + char *bdf; + int i, rc = 0; + + sp = spdk_conf_find_section(NULL, "Global"); + + if (opts->shm_id == -1) { + if (sp != NULL) { + opts->shm_id = spdk_conf_section_get_intval(sp, "SharedMemoryID"); + } + } + + if (opts->reactor_mask == NULL) { + if (sp && spdk_conf_section_get_val(sp, "ReactorMask")) { + SPDK_ERRLOG("ReactorMask config option is deprecated. Use -m/--cpumask\n" + "command line parameter instead.\n"); + opts->reactor_mask = spdk_conf_section_get_val(sp, "ReactorMask"); + } else { + opts->reactor_mask = SPDK_APP_DPDK_DEFAULT_CORE_MASK; + } + } + + if (!opts->no_pci && sp) { + opts->no_pci = spdk_conf_section_get_boolval(sp, "NoPci", false); + } + + if (opts->tpoint_group_mask == NULL) { + if (sp != NULL) { + opts->tpoint_group_mask = spdk_conf_section_get_val(sp, "TpointGroupMask"); + } + } + + if (sp == NULL) { + return 0; + } + + for (i = 0; ; i++) { + bdf = spdk_conf_section_get_nmval(sp, "PciBlacklist", i, 0); + if (!bdf) { + break; + } + + rc = spdk_app_opts_add_pci_addr(opts, &opts->pci_blacklist, bdf); + if (rc != 0) { + free(opts->pci_blacklist); + return rc; + } + } + + for (i = 0; ; i++) { + bdf = spdk_conf_section_get_nmval(sp, "PciWhitelist", i, 0); + if (!bdf) { + break; + } + + if (opts->pci_blacklist != NULL) { + SPDK_ERRLOG("PciBlacklist and PciWhitelist cannot be used at the same time\n"); + free(opts->pci_blacklist); + return -EINVAL; + } + + rc = spdk_app_opts_add_pci_addr(opts, &opts->pci_whitelist, bdf); + if (rc != 0) { + free(opts->pci_whitelist); + return rc; + } + } + return 0; +} + +static int +spdk_app_setup_env(struct spdk_app_opts *opts) +{ + struct spdk_env_opts env_opts = {}; + int rc; + + spdk_env_opts_init(&env_opts); + + env_opts.name = opts->name; + env_opts.core_mask = opts->reactor_mask; + env_opts.shm_id = opts->shm_id; + env_opts.mem_channel = opts->mem_channel; + env_opts.master_core = opts->master_core; + env_opts.mem_size = opts->mem_size; + env_opts.hugepage_single_segments = opts->hugepage_single_segments; + env_opts.unlink_hugepage = opts->unlink_hugepage; + env_opts.no_pci = opts->no_pci; + env_opts.num_pci_addr = opts->num_pci_addr; + env_opts.pci_blacklist = opts->pci_blacklist; + env_opts.pci_whitelist = opts->pci_whitelist; + + rc = spdk_env_init(&env_opts); + free(env_opts.pci_blacklist); + free(env_opts.pci_whitelist); + + if (rc < 0) { + fprintf(stderr, "Unable to initialize SPDK env\n"); + } + + return rc; +} + +static int +spdk_app_setup_trace(struct spdk_app_opts *opts) +{ + char shm_name[64]; + uint64_t tpoint_group_mask; + char *end; + + if (opts->shm_id >= 0) { + snprintf(shm_name, sizeof(shm_name), "/%s_trace.%d", opts->name, opts->shm_id); + } else { + snprintf(shm_name, sizeof(shm_name), "/%s_trace.pid%d", opts->name, (int)getpid()); + } + + if (spdk_trace_init(shm_name) != 0) { + return -1; + } + + if (opts->tpoint_group_mask != NULL) { + errno = 0; + tpoint_group_mask = strtoull(opts->tpoint_group_mask, &end, 16); + if (*end != '\0' || errno) { + SPDK_ERRLOG("invalid tpoint mask %s\n", opts->tpoint_group_mask); + } else { + SPDK_NOTICELOG("Tracepoint Group Mask %s specified.\n", opts->tpoint_group_mask); + SPDK_NOTICELOG("Use 'spdk_trace -s %s %s %d' to capture a snapshot of events at runtime.\n", + opts->name, + opts->shm_id >= 0 ? "-i" : "-p", + opts->shm_id >= 0 ? opts->shm_id : getpid()); +#if defined(__linux__) + SPDK_NOTICELOG("Or copy /dev/shm%s for offline analysis/debug.\n", shm_name); +#endif + spdk_trace_set_tpoint_group_mask(tpoint_group_mask); + } + } + + return 0; +} + +int +spdk_app_start(struct spdk_app_opts *opts, spdk_event_fn start_fn, + void *arg1, void *arg2) +{ + struct spdk_conf *config = NULL; + int rc; + struct spdk_event *rpc_start_event; + char *tty; + + if (!opts) { + SPDK_ERRLOG("opts should not be NULL\n"); + return 1; + } + + if (!start_fn) { + SPDK_ERRLOG("start_fn should not be NULL\n"); + return 1; + } + + tty = ttyname(STDERR_FILENO); + if (opts->print_level > SPDK_LOG_WARN && + isatty(STDERR_FILENO) && + tty && + !strncmp(tty, "/dev/tty", strlen("/dev/tty"))) { + printf("Warning: printing stderr to console terminal without -q option specified.\n"); + printf("Suggest using --silence-noticelog to disable logging to stderr and\n"); + printf("monitor syslog, or redirect stderr to a file.\n"); + printf("(Delaying for 10 seconds...)\n"); + sleep(10); + } + + spdk_log_set_print_level(opts->print_level); + +#ifndef SPDK_NO_RLIMIT + if (opts->enable_coredump) { + struct rlimit core_limits; + + core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY; + setrlimit(RLIMIT_CORE, &core_limits); + } +#endif + + config = spdk_app_setup_conf(opts->config_file); + if (config == NULL) { + goto app_start_setup_conf_err; + } + + if (spdk_app_read_config_file_global_params(opts) < 0) { + goto app_start_setup_conf_err; + } + + spdk_log_set_level(SPDK_APP_DEFAULT_LOG_LEVEL); + spdk_log_set_backtrace_level(SPDK_APP_DEFAULT_BACKTRACE_LOG_LEVEL); + + if (spdk_app_setup_env(opts) < 0) { + goto app_start_setup_conf_err; + } + + spdk_log_open(); + SPDK_NOTICELOG("Total cores available: %d\n", spdk_env_get_core_count()); + + spdk_thread_lib_init(); + + /* + * If mask not specified on command line or in configuration file, + * reactor_mask will be 0x1 which will enable core 0 to run one + * reactor. + */ + if ((rc = spdk_reactors_init(opts->max_delay_us)) != 0) { + SPDK_ERRLOG("Invalid reactor mask.\n"); + goto app_start_log_close_err; + } + + /* + * Note the call to spdk_app_setup_trace() is located here + * ahead of spdk_app_setup_signal_handlers(). + * That's because there is not an easy/direct clean + * way of unwinding alloc'd resources that can occur + * in spdk_app_setup_signal_handlers(). + */ + if (spdk_app_setup_trace(opts) != 0) { + goto app_start_log_close_err; + } + + if ((rc = spdk_app_setup_signal_handlers(opts)) != 0) { + goto app_start_trace_cleanup_err; + } + + memset(&g_spdk_app, 0, sizeof(g_spdk_app)); + g_spdk_app.config = config; + g_spdk_app.shm_id = opts->shm_id; + g_spdk_app.shutdown_cb = opts->shutdown_cb; + g_spdk_app.rc = 0; + g_init_lcore = spdk_env_get_current_core(); + g_delay_subsystem_init = opts->delay_subsystem_init; + g_app_start_event = spdk_event_allocate(g_init_lcore, start_fn, arg1, arg2); + + rpc_start_event = spdk_event_allocate(g_init_lcore, spdk_app_start_rpc, + (void *)opts->rpc_addr, NULL); + + if (!g_delay_subsystem_init) { + spdk_subsystem_init(rpc_start_event); + } else { + spdk_event_call(rpc_start_event); + } + + /* This blocks until spdk_app_stop is called */ + spdk_reactors_start(); + + return g_spdk_app.rc; + +app_start_trace_cleanup_err: + spdk_trace_cleanup(); + +app_start_log_close_err: + spdk_log_close(); + +app_start_setup_conf_err: + return 1; +} + +void +spdk_app_fini(void) +{ + spdk_trace_cleanup(); + spdk_reactors_fini(); + spdk_conf_free(g_spdk_app.config); + spdk_log_close(); + spdk_thread_lib_fini(); +} + +static void +_spdk_app_stop(void *arg1, void *arg2) +{ + struct spdk_event *app_stop_event; + + spdk_rpc_finish(); + + app_stop_event = spdk_event_allocate(spdk_env_get_current_core(), spdk_reactors_stop, NULL, NULL); + spdk_subsystem_fini(app_stop_event); +} + +void +spdk_app_stop(int rc) +{ + if (rc) { + SPDK_WARNLOG("spdk_app_stop'd on non-zero\n"); + } + g_spdk_app.rc = rc; + /* + * We want to run spdk_subsystem_fini() from the same lcore where spdk_subsystem_init() + * was called. + */ + spdk_event_call(spdk_event_allocate(g_init_lcore, _spdk_app_stop, NULL, NULL)); +} + +static void +usage(void (*app_usage)(void)) +{ + printf("%s [options]\n", g_executable_name); + printf("options:\n"); + printf(" -c, --config <config> config file (default %s)\n", g_default_opts.config_file); + printf(" -d, --limit-coredump do not set max coredump size to RLIM_INFINITY\n"); + printf(" -e, --tpoint-group-mask <mask>\n"); + printf(" tracepoint group mask for spdk trace buffers (default 0x0)\n"); + printf(" -g, --single-file-segments\n"); + printf(" force creating just one hugetlbfs file\n"); + printf(" -h, --help show this usage\n"); + printf(" -i, --shm-id <id> shared memory ID (optional)\n"); + printf(" -m, --cpumask <mask> core mask for DPDK\n"); + printf(" -n, --mem-channels <num> channel number of memory channels used for DPDK\n"); + printf(" -p, --master-core <id> master (primary) core for DPDK\n"); + printf(" -r, --rpc-socket <path> RPC listen address (default %s)\n", SPDK_DEFAULT_RPC_ADDR); + printf(" -s, --mem-size <size> memory size in MB for DPDK (default: "); + if (g_default_opts.mem_size > 0) { + printf("%dMB)\n", g_default_opts.mem_size); + } else { + printf("all hugepage memory)\n"); + } + printf(" --silence-noticelog disable notice level logging to stderr\n"); + printf(" -u, --no-pci disable PCI access\n"); + printf(" --wait-for-rpc wait for RPCs to initialize subsystems\n"); + printf(" -B, --pci-blacklist <bdf>\n"); + printf(" pci addr to blacklist (can be used more than once)\n"); + printf(" -R, --huge-unlink unlink huge files after initialization\n"); + printf(" -W, --pci-whitelist <bdf>\n"); + printf(" pci addr to whitelist (-B and -W cannot be used at the same time)\n"); + spdk_tracelog_usage(stdout, "-L"); + if (app_usage) { + app_usage(); + } +} + +spdk_app_parse_args_rvals_t +spdk_app_parse_args(int argc, char **argv, struct spdk_app_opts *opts, + const char *app_getopt_str, struct option *app_long_opts, + void (*app_parse)(int ch, char *arg), + void (*app_usage)(void)) +{ + int ch, rc, opt_idx, global_long_opts_len, app_long_opts_len; + struct option *cmdline_options; + char *cmdline_short_opts = NULL; + enum spdk_app_parse_args_rvals retval = SPDK_APP_PARSE_ARGS_FAIL; + + memcpy(&g_default_opts, opts, sizeof(g_default_opts)); + + if (opts->config_file && access(opts->config_file, F_OK) != 0) { + opts->config_file = NULL; + } + + if (app_long_opts == NULL) { + app_long_opts_len = 0; + } else { + for (app_long_opts_len = 0; + app_long_opts[app_long_opts_len].name != NULL; + app_long_opts_len++); + } + + global_long_opts_len = SPDK_COUNTOF(g_cmdline_options); + + cmdline_options = calloc(global_long_opts_len + app_long_opts_len + 1, sizeof(*cmdline_options)); + if (!cmdline_options) { + fprintf(stderr, "Out of memory\n"); + return SPDK_APP_PARSE_ARGS_FAIL; + } + + memcpy(&cmdline_options[0], g_cmdline_options, sizeof(g_cmdline_options)); + if (app_long_opts) { + memcpy(&cmdline_options[global_long_opts_len], app_long_opts, + app_long_opts_len * sizeof(*app_long_opts)); + } + + if (app_getopt_str != NULL) { + ch = spdk_app_opts_validate(app_getopt_str); + if (ch) { + fprintf(stderr, "Duplicated option '%c' between the generic and application specific spdk opts.\n", + ch); + goto out; + } + } + + cmdline_short_opts = spdk_sprintf_alloc("%s%s", app_getopt_str, SPDK_APP_GETOPT_STRING); + if (!cmdline_short_opts) { + fprintf(stderr, "Out of memory\n"); + goto out; + } + + g_executable_name = argv[0]; + + while ((ch = getopt_long(argc, argv, cmdline_short_opts, cmdline_options, &opt_idx)) != -1) { + switch (ch) { + case CONFIG_FILE_OPT_IDX: + opts->config_file = optarg; + break; + case LIMIT_COREDUMP_OPT_IDX: + opts->enable_coredump = false; + break; + case TPOINT_GROUP_MASK_OPT_IDX: + opts->tpoint_group_mask = optarg; + break; + case SINGLE_FILE_SEGMENTS_OPT_IDX: + opts->hugepage_single_segments = true; + break; + case HELP_OPT_IDX: + usage(app_usage); + retval = SPDK_APP_PARSE_ARGS_HELP; + goto out; + case SHM_ID_OPT_IDX: + if (optarg == NULL) { + goto out; + } + opts->shm_id = atoi(optarg); + break; + case CPUMASK_OPT_IDX: + opts->reactor_mask = optarg; + break; + case MEM_CHANNELS_OPT_IDX: + if (optarg == NULL) { + goto out; + } + opts->mem_channel = atoi(optarg); + break; + case MASTER_CORE_OPT_IDX: + if (optarg == NULL) { + goto out; + } + opts->master_core = atoi(optarg); + break; + case SILENCE_NOTICELOG_OPT_IDX: + opts->print_level = SPDK_LOG_WARN; + break; + case RPC_SOCKET_OPT_IDX: + opts->rpc_addr = optarg; + break; + case MEM_SIZE_OPT_IDX: { + uint64_t mem_size_mb; + bool mem_size_has_prefix; + + rc = spdk_parse_capacity(optarg, &mem_size_mb, &mem_size_has_prefix); + if (rc != 0) { + fprintf(stderr, "invalid memory pool size `-s %s`\n", optarg); + usage(app_usage); + goto out; + } + + if (mem_size_has_prefix) { + /* the mem size is in MB by default, so if a prefix was + * specified, we need to manually convert to MB. + */ + mem_size_mb /= 1024 * 1024; + } + + if (mem_size_mb > INT_MAX) { + fprintf(stderr, "invalid memory pool size `-s %s`\n", optarg); + usage(app_usage); + goto out; + } + + opts->mem_size = (int) mem_size_mb; + break; + } + case NO_PCI_OPT_IDX: + opts->no_pci = true; + break; + case WAIT_FOR_RPC_OPT_IDX: + opts->delay_subsystem_init = true; + break; + case PCI_BLACKLIST_OPT_IDX: + if (opts->pci_whitelist) { + free(opts->pci_whitelist); + opts->pci_whitelist = NULL; + fprintf(stderr, "-B and -W cannot be used at the same time\n"); + usage(app_usage); + goto out; + } + + rc = spdk_app_opts_add_pci_addr(opts, &opts->pci_blacklist, optarg); + if (rc != 0) { + free(opts->pci_blacklist); + opts->pci_blacklist = NULL; + goto out; + } + break; + case TRACEFLAG_OPT_IDX: +#ifndef DEBUG + fprintf(stderr, "%s must be built with CONFIG_DEBUG=y for -L flag\n", + argv[0]); + usage(app_usage); + goto out; +#else + rc = spdk_log_set_trace_flag(optarg); + if (rc < 0) { + fprintf(stderr, "unknown flag\n"); + usage(app_usage); + goto out; + } + opts->print_level = SPDK_LOG_DEBUG; + break; +#endif + case HUGE_UNLINK_OPT_IDX: + opts->unlink_hugepage = true; + break; + case PCI_WHITELIST_OPT_IDX: + if (opts->pci_blacklist) { + free(opts->pci_blacklist); + opts->pci_blacklist = NULL; + fprintf(stderr, "-B and -W cannot be used at the same time\n"); + usage(app_usage); + goto out; + } + + rc = spdk_app_opts_add_pci_addr(opts, &opts->pci_whitelist, optarg); + if (rc != 0) { + free(opts->pci_whitelist); + opts->pci_whitelist = NULL; + goto out; + } + break; + case '?': + /* + * In the event getopt() above detects an option + * in argv that is NOT in the getopt_str, + * getopt() will return a '?' indicating failure. + */ + usage(app_usage); + goto out; + default: + app_parse(ch, optarg); + } + } + + /* TBD: Replace warning by failure when RPCs for startup are prepared. */ + if (opts->config_file && opts->delay_subsystem_init) { + fprintf(stderr, + "WARNING: --wait-for-rpc and config file are used at the same time. " + "- Please be careful one options might overwrite others.\n"); + } + + retval = SPDK_APP_PARSE_ARGS_SUCCESS; +out: + if (retval != SPDK_APP_PARSE_ARGS_SUCCESS) { + free(opts->pci_blacklist); + opts->pci_blacklist = NULL; + free(opts->pci_whitelist); + opts->pci_whitelist = NULL; + } + free(cmdline_short_opts); + free(cmdline_options); + return retval; +} + +void +spdk_app_usage(void) +{ + if (g_executable_name == NULL) { + fprintf(stderr, "%s not valid before calling spdk_app_parse_args()\n", __func__); + return; + } + + usage(NULL); +} + +static void +spdk_rpc_start_subsystem_init_cpl(void *arg1, void *arg2) +{ + struct spdk_jsonrpc_request *request = arg1; + struct spdk_json_write_ctx *w; + + spdk_app_start_application(); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static void +spdk_rpc_start_subsystem_init(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct spdk_event *cb_event; + + if (params != NULL) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "start_subsystem_init requires no parameters"); + return; + } + + cb_event = spdk_event_allocate(g_init_lcore, spdk_rpc_start_subsystem_init_cpl, + request, NULL); + spdk_subsystem_init(cb_event); +} +SPDK_RPC_REGISTER("start_subsystem_init", spdk_rpc_start_subsystem_init, SPDK_RPC_STARTUP) diff --git a/src/spdk/lib/event/reactor.c b/src/spdk/lib/event/reactor.c new file mode 100644 index 00000000..d9ba9f6b --- /dev/null +++ b/src/spdk/lib/event/reactor.c @@ -0,0 +1,804 @@ +/*- + * 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/likely.h" + +#include "spdk_internal/event.h" +#include "spdk_internal/log.h" + +#include "spdk/log.h" +#include "spdk/thread.h" +#include "spdk/env.h" +#include "spdk/util.h" + +#define SPDK_MAX_SOCKET 64 + +#define SPDK_EVENT_BATCH_SIZE 8 + +enum spdk_poller_state { + /* The poller is registered with a reactor but not currently executing its fn. */ + SPDK_POLLER_STATE_WAITING, + + /* The poller is currently running its fn. */ + SPDK_POLLER_STATE_RUNNING, + + /* The poller was unregistered during the execution of its fn. */ + SPDK_POLLER_STATE_UNREGISTERED, +}; + +struct spdk_poller { + TAILQ_ENTRY(spdk_poller) tailq; + uint32_t lcore; + + /* Current state of the poller; should only be accessed from the poller's thread. */ + enum spdk_poller_state state; + + uint64_t period_ticks; + uint64_t next_run_tick; + spdk_poller_fn fn; + void *arg; +}; + +enum spdk_reactor_state { + SPDK_REACTOR_STATE_INVALID = 0, + SPDK_REACTOR_STATE_INITIALIZED = 1, + SPDK_REACTOR_STATE_RUNNING = 2, + SPDK_REACTOR_STATE_EXITING = 3, + SPDK_REACTOR_STATE_SHUTDOWN = 4, +}; + +struct spdk_reactor { + /* Logical core number for this reactor. */ + uint32_t lcore; + + /* Socket ID for this reactor. */ + uint32_t socket_id; + + /* Poller for get the rusage for the reactor. */ + struct spdk_poller *rusage_poller; + + /* Reactor tsc stats */ + struct spdk_reactor_tsc_stats tsc_stats; + + uint64_t tsc_last; + + /* The last known rusage values */ + struct rusage rusage; + + /* + * Contains pollers actively running on this reactor. Pollers + * are run round-robin. The reactor takes one poller from the head + * of the ring, executes it, then puts it back at the tail of + * the ring. + */ + TAILQ_HEAD(, spdk_poller) active_pollers; + + /** + * Contains pollers running on this reactor with a periodic timer. + */ + TAILQ_HEAD(timer_pollers_head, spdk_poller) timer_pollers; + + struct spdk_ring *events; + + /* Pointer to the per-socket g_spdk_event_mempool for this reactor. */ + struct spdk_mempool *event_mempool; + + uint64_t max_delay_us; +} __attribute__((aligned(64))); + +static struct spdk_reactor *g_reactors; + +static enum spdk_reactor_state g_reactor_state = SPDK_REACTOR_STATE_INVALID; + +static bool g_context_switch_monitor_enabled = true; + +static void spdk_reactor_construct(struct spdk_reactor *w, uint32_t lcore, + uint64_t max_delay_us); + +static struct spdk_mempool *g_spdk_event_mempool[SPDK_MAX_SOCKET]; + +static struct spdk_cpuset *g_spdk_app_core_mask; + +static struct spdk_reactor * +spdk_reactor_get(uint32_t lcore) +{ + struct spdk_reactor *reactor; + reactor = spdk_likely(g_reactors) ? &g_reactors[lcore] : NULL; + return reactor; +} + +struct spdk_event * +spdk_event_allocate(uint32_t lcore, spdk_event_fn fn, void *arg1, void *arg2) +{ + struct spdk_event *event = NULL; + struct spdk_reactor *reactor = spdk_reactor_get(lcore); + + if (!reactor) { + assert(false); + return NULL; + } + + event = spdk_mempool_get(reactor->event_mempool); + if (event == NULL) { + assert(false); + return NULL; + } + + event->lcore = lcore; + event->fn = fn; + event->arg1 = arg1; + event->arg2 = arg2; + + return event; +} + +void +spdk_event_call(struct spdk_event *event) +{ + int rc; + struct spdk_reactor *reactor; + + reactor = spdk_reactor_get(event->lcore); + + assert(reactor->events != NULL); + rc = spdk_ring_enqueue(reactor->events, (void **)&event, 1); + if (rc != 1) { + assert(false); + } +} + +static inline uint32_t +_spdk_event_queue_run_batch(struct spdk_reactor *reactor) +{ + unsigned count, i; + void *events[SPDK_EVENT_BATCH_SIZE]; + +#ifdef DEBUG + /* + * spdk_ring_dequeue() fills events and returns how many entries it wrote, + * so we will never actually read uninitialized data from events, but just to be sure + * (and to silence a static analyzer false positive), initialize the array to NULL pointers. + */ + memset(events, 0, sizeof(events)); +#endif + + count = spdk_ring_dequeue(reactor->events, events, SPDK_EVENT_BATCH_SIZE); + if (count == 0) { + return 0; + } + + for (i = 0; i < count; i++) { + struct spdk_event *event = events[i]; + + assert(event != NULL); + event->fn(event->arg1, event->arg2); + } + + spdk_mempool_put_bulk(reactor->event_mempool, events, count); + + return count; +} + +static void +_spdk_reactor_msg_passed(void *arg1, void *arg2) +{ + spdk_thread_fn fn = arg1; + + fn(arg2); +} + +static void +_spdk_reactor_send_msg(spdk_thread_fn fn, void *ctx, void *thread_ctx) +{ + struct spdk_event *event; + struct spdk_reactor *reactor; + + reactor = thread_ctx; + + event = spdk_event_allocate(reactor->lcore, _spdk_reactor_msg_passed, fn, ctx); + + spdk_event_call(event); +} + +static void +_spdk_poller_insert_timer(struct spdk_reactor *reactor, struct spdk_poller *poller, uint64_t now) +{ + struct spdk_poller *iter; + uint64_t next_run_tick; + + next_run_tick = now + poller->period_ticks; + poller->next_run_tick = next_run_tick; + + /* + * Insert poller in the reactor's timer_pollers list in sorted order by next scheduled + * run time. + */ + TAILQ_FOREACH_REVERSE(iter, &reactor->timer_pollers, timer_pollers_head, tailq) { + if (iter->next_run_tick <= next_run_tick) { + TAILQ_INSERT_AFTER(&reactor->timer_pollers, iter, poller, tailq); + return; + } + } + + /* No earlier pollers were found, so this poller must be the new head */ + TAILQ_INSERT_HEAD(&reactor->timer_pollers, poller, tailq); +} + +static struct spdk_poller * +_spdk_reactor_start_poller(void *thread_ctx, + spdk_poller_fn fn, + void *arg, + uint64_t period_microseconds) +{ + struct spdk_poller *poller; + struct spdk_reactor *reactor; + uint64_t quotient, remainder, ticks; + + reactor = thread_ctx; + + poller = calloc(1, sizeof(*poller)); + if (poller == NULL) { + SPDK_ERRLOG("Poller memory allocation failed\n"); + return NULL; + } + + poller->lcore = reactor->lcore; + poller->state = SPDK_POLLER_STATE_WAITING; + poller->fn = fn; + poller->arg = arg; + + if (period_microseconds) { + quotient = period_microseconds / SPDK_SEC_TO_USEC; + remainder = period_microseconds % SPDK_SEC_TO_USEC; + ticks = spdk_get_ticks_hz(); + + poller->period_ticks = ticks * quotient + (ticks * remainder) / SPDK_SEC_TO_USEC; + } else { + poller->period_ticks = 0; + } + + if (poller->period_ticks) { + _spdk_poller_insert_timer(reactor, poller, spdk_get_ticks()); + } else { + TAILQ_INSERT_TAIL(&reactor->active_pollers, poller, tailq); + } + + return poller; +} + +static void +_spdk_reactor_stop_poller(struct spdk_poller *poller, void *thread_ctx) +{ + struct spdk_reactor *reactor; + + reactor = thread_ctx; + + assert(poller->lcore == spdk_env_get_current_core()); + + if (poller->state == SPDK_POLLER_STATE_RUNNING) { + /* + * We are being called from the poller_fn, so set the state to unregistered + * and let the reactor loop free the poller. + */ + poller->state = SPDK_POLLER_STATE_UNREGISTERED; + } else { + /* Poller is not running currently, so just free it. */ + if (poller->period_ticks) { + TAILQ_REMOVE(&reactor->timer_pollers, poller, tailq); + } else { + TAILQ_REMOVE(&reactor->active_pollers, poller, tailq); + } + + free(poller); + } +} + +static int +get_rusage(void *arg) +{ + struct spdk_reactor *reactor = arg; + struct rusage rusage; + + if (getrusage(RUSAGE_THREAD, &rusage) != 0) { + return -1; + } + + if (rusage.ru_nvcsw != reactor->rusage.ru_nvcsw || rusage.ru_nivcsw != reactor->rusage.ru_nivcsw) { + SPDK_INFOLOG(SPDK_LOG_REACTOR, + "Reactor %d: %ld voluntary context switches and %ld involuntary context switches in the last second.\n", + reactor->lcore, rusage.ru_nvcsw - reactor->rusage.ru_nvcsw, + rusage.ru_nivcsw - reactor->rusage.ru_nivcsw); + } + reactor->rusage = rusage; + + return -1; +} + +static void +_spdk_reactor_context_switch_monitor_start(void *arg1, void *arg2) +{ + struct spdk_reactor *reactor = arg1; + + if (reactor->rusage_poller == NULL) { + getrusage(RUSAGE_THREAD, &reactor->rusage); + reactor->rusage_poller = spdk_poller_register(get_rusage, reactor, 1000000); + } +} + +static void +_spdk_reactor_context_switch_monitor_stop(void *arg1, void *arg2) +{ + struct spdk_reactor *reactor = arg1; + + if (reactor->rusage_poller != NULL) { + spdk_poller_unregister(&reactor->rusage_poller); + } +} + +static size_t +_spdk_reactor_get_max_event_cnt(uint8_t socket_count) +{ + size_t cnt; + + /* Try to make event ring fill at most 2MB of memory, + * as some ring implementations may require physical address + * contingency. We don't want to introduce a requirement of + * at least 2 physically contiguous 2MB hugepages. + */ + cnt = spdk_min(262144 / socket_count, 262144 / 2); + /* Take into account one extra element required by + * some ring implementations. + */ + cnt -= 1; + return cnt; +} + +void +spdk_reactor_enable_context_switch_monitor(bool enable) +{ + struct spdk_reactor *reactor; + spdk_event_fn fn; + uint32_t core; + + if (enable != g_context_switch_monitor_enabled) { + g_context_switch_monitor_enabled = enable; + if (enable) { + fn = _spdk_reactor_context_switch_monitor_start; + } else { + fn = _spdk_reactor_context_switch_monitor_stop; + } + SPDK_ENV_FOREACH_CORE(core) { + reactor = spdk_reactor_get(core); + spdk_event_call(spdk_event_allocate(core, fn, reactor, NULL)); + } + } +} + +bool +spdk_reactor_context_switch_monitor_enabled(void) +{ + return g_context_switch_monitor_enabled; +} + +static void +spdk_reactor_add_tsc_stats(void *arg, int rc, uint64_t now) +{ + struct spdk_reactor *reactor = arg; + struct spdk_reactor_tsc_stats *tsc_stats = &reactor->tsc_stats; + + if (rc == 0) { + /* Poller status idle */ + tsc_stats->idle_tsc += now - reactor->tsc_last; + } else if (rc > 0) { + /* Poller status busy */ + tsc_stats->busy_tsc += now - reactor->tsc_last; + } else { + /* Poller status unknown */ + tsc_stats->unknown_tsc += now - reactor->tsc_last; + } + + reactor->tsc_last = now; +} + +int +spdk_reactor_get_tsc_stats(struct spdk_reactor_tsc_stats *tsc_stats, uint32_t core) +{ + struct spdk_reactor *reactor; + + if (!spdk_cpuset_get_cpu(g_spdk_app_core_mask, core)) { + return -1; + } + + reactor = spdk_reactor_get(core); + *tsc_stats = reactor->tsc_stats; + + return 0; +} + +/** + * + * \brief This is the main function of the reactor thread. + * + * \code + * + * while (1) + * if (events to run) + * dequeue and run a batch of events + * + * if (active pollers) + * run the first poller in the list and move it to the back + * + * if (first timer poller has expired) + * run the first timer poller and reinsert it in the timer list + * + * if (no action taken and sleep enabled) + * sleep until next timer poller is scheduled to expire + * \endcode + * + */ +static int +_spdk_reactor_run(void *arg) +{ + struct spdk_reactor *reactor = arg; + struct spdk_poller *poller; + uint32_t event_count; + uint64_t now; + uint64_t sleep_cycles; + uint32_t sleep_us; + int rc = -1; + char thread_name[32]; + + snprintf(thread_name, sizeof(thread_name), "reactor_%u", reactor->lcore); + if (spdk_allocate_thread(_spdk_reactor_send_msg, + _spdk_reactor_start_poller, + _spdk_reactor_stop_poller, + reactor, thread_name) == NULL) { + return -1; + } + SPDK_NOTICELOG("Reactor started on core %u on socket %u\n", reactor->lcore, + reactor->socket_id); + + sleep_cycles = reactor->max_delay_us * spdk_get_ticks_hz() / SPDK_SEC_TO_USEC; + if (g_context_switch_monitor_enabled) { + _spdk_reactor_context_switch_monitor_start(reactor, NULL); + } + now = spdk_get_ticks(); + reactor->tsc_last = now; + + while (1) { + bool took_action = false; + + event_count = _spdk_event_queue_run_batch(reactor); + if (event_count > 0) { + rc = 1; + now = spdk_get_ticks(); + spdk_reactor_add_tsc_stats(reactor, rc, now); + took_action = true; + } + + poller = TAILQ_FIRST(&reactor->active_pollers); + if (poller) { + TAILQ_REMOVE(&reactor->active_pollers, poller, tailq); + poller->state = SPDK_POLLER_STATE_RUNNING; + rc = poller->fn(poller->arg); + now = spdk_get_ticks(); + spdk_reactor_add_tsc_stats(reactor, rc, now); + if (poller->state == SPDK_POLLER_STATE_UNREGISTERED) { + free(poller); + } else { + poller->state = SPDK_POLLER_STATE_WAITING; + TAILQ_INSERT_TAIL(&reactor->active_pollers, poller, tailq); + } + took_action = true; + } + + poller = TAILQ_FIRST(&reactor->timer_pollers); + if (poller) { + if (took_action == false) { + now = spdk_get_ticks(); + } + + if (now >= poller->next_run_tick) { + uint64_t tmp_timer_tsc; + + TAILQ_REMOVE(&reactor->timer_pollers, poller, tailq); + poller->state = SPDK_POLLER_STATE_RUNNING; + rc = poller->fn(poller->arg); + /* Save the tsc value from before poller->fn was executed. We want to + * use the current time for idle/busy tsc value accounting, but want to + * use the older time to reinsert to the timer poller below. */ + tmp_timer_tsc = now; + now = spdk_get_ticks(); + spdk_reactor_add_tsc_stats(reactor, rc, now); + if (poller->state == SPDK_POLLER_STATE_UNREGISTERED) { + free(poller); + } else { + poller->state = SPDK_POLLER_STATE_WAITING; + _spdk_poller_insert_timer(reactor, poller, tmp_timer_tsc); + } + took_action = true; + } + } + + /* Determine if the thread can sleep */ + if (sleep_cycles && !took_action) { + now = spdk_get_ticks(); + sleep_us = reactor->max_delay_us; + + poller = TAILQ_FIRST(&reactor->timer_pollers); + if (poller) { + /* There are timers registered, so don't sleep beyond + * when the next timer should fire */ + if (poller->next_run_tick < (now + sleep_cycles)) { + if (poller->next_run_tick <= now) { + sleep_us = 0; + } else { + sleep_us = ((poller->next_run_tick - now) * + SPDK_SEC_TO_USEC) / spdk_get_ticks_hz(); + } + } + } + + if (sleep_us > 0) { + usleep(sleep_us); + } + } + + if (g_reactor_state != SPDK_REACTOR_STATE_RUNNING) { + break; + } + } + + _spdk_reactor_context_switch_monitor_stop(reactor, NULL); + spdk_free_thread(); + return 0; +} + +static void +spdk_reactor_construct(struct spdk_reactor *reactor, uint32_t lcore, uint64_t max_delay_us) +{ + reactor->lcore = lcore; + reactor->socket_id = spdk_env_get_socket_id(lcore); + assert(reactor->socket_id < SPDK_MAX_SOCKET); + reactor->max_delay_us = max_delay_us; + + TAILQ_INIT(&reactor->active_pollers); + TAILQ_INIT(&reactor->timer_pollers); + + reactor->events = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 65536, reactor->socket_id); + if (!reactor->events) { + SPDK_NOTICELOG("Ring creation failed on preferred socket %d. Try other sockets.\n", + reactor->socket_id); + + reactor->events = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 65536, + SPDK_ENV_SOCKET_ID_ANY); + } + assert(reactor->events != NULL); + + reactor->event_mempool = g_spdk_event_mempool[reactor->socket_id]; +} + +int +spdk_app_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask) +{ + int ret; + struct spdk_cpuset *validmask; + + ret = spdk_cpuset_parse(cpumask, mask); + if (ret < 0) { + return ret; + } + + validmask = spdk_app_get_core_mask(); + spdk_cpuset_and(cpumask, validmask); + + return 0; +} + +struct spdk_cpuset * +spdk_app_get_core_mask(void) +{ + return g_spdk_app_core_mask; +} + + +static uint64_t +spdk_reactor_get_socket_mask(void) +{ + uint32_t i; + uint32_t socket_id; + uint64_t socket_info = 0; + + SPDK_ENV_FOREACH_CORE(i) { + socket_id = spdk_env_get_socket_id(i); + socket_info |= (1ULL << socket_id); + } + + return socket_info; +} + +void +spdk_reactors_start(void) +{ + struct spdk_reactor *reactor; + uint32_t i, current_core; + int rc; + + g_reactor_state = SPDK_REACTOR_STATE_RUNNING; + g_spdk_app_core_mask = spdk_cpuset_alloc(); + + current_core = spdk_env_get_current_core(); + SPDK_ENV_FOREACH_CORE(i) { + if (i != current_core) { + reactor = spdk_reactor_get(i); + rc = spdk_env_thread_launch_pinned(reactor->lcore, _spdk_reactor_run, reactor); + if (rc < 0) { + SPDK_ERRLOG("Unable to start reactor thread on core %u\n", reactor->lcore); + assert(false); + return; + } + } + spdk_cpuset_set_cpu(g_spdk_app_core_mask, i, true); + } + + /* Start the master reactor */ + reactor = spdk_reactor_get(current_core); + _spdk_reactor_run(reactor); + + spdk_env_thread_wait_all(); + + g_reactor_state = SPDK_REACTOR_STATE_SHUTDOWN; + spdk_cpuset_free(g_spdk_app_core_mask); + g_spdk_app_core_mask = NULL; +} + +void +spdk_reactors_stop(void *arg1, void *arg2) +{ + g_reactor_state = SPDK_REACTOR_STATE_EXITING; +} + +int +spdk_reactors_init(unsigned int max_delay_us) +{ + int rc; + uint32_t i, j, last_core; + struct spdk_reactor *reactor; + uint64_t socket_mask = 0x0; + uint8_t socket_count = 0; + char mempool_name[32]; + + socket_mask = spdk_reactor_get_socket_mask(); + SPDK_NOTICELOG("Occupied cpu socket mask is 0x%lx\n", socket_mask); + + for (i = 0; i < SPDK_MAX_SOCKET; i++) { + if ((1ULL << i) & socket_mask) { + socket_count++; + } + } + if (socket_count == 0) { + SPDK_ERRLOG("No sockets occupied (internal error)\n"); + return -1; + } + + for (i = 0; i < SPDK_MAX_SOCKET; i++) { + if ((1ULL << i) & socket_mask) { + snprintf(mempool_name, sizeof(mempool_name), "evtpool%d_%d", i, getpid()); + g_spdk_event_mempool[i] = spdk_mempool_create(mempool_name, + _spdk_reactor_get_max_event_cnt(socket_count), + sizeof(struct spdk_event), + SPDK_MEMPOOL_DEFAULT_CACHE_SIZE, i); + + if (g_spdk_event_mempool[i] == NULL) { + SPDK_NOTICELOG("Event_mempool creation failed on preferred socket %d.\n", i); + + /* + * Instead of failing the operation directly, try to create + * the mempool on any available sockets in the case that + * memory is not evenly installed on all sockets. If still + * fails, free all allocated memory and exits. + */ + g_spdk_event_mempool[i] = spdk_mempool_create( + mempool_name, + _spdk_reactor_get_max_event_cnt(socket_count), + sizeof(struct spdk_event), + SPDK_MEMPOOL_DEFAULT_CACHE_SIZE, + SPDK_ENV_SOCKET_ID_ANY); + + if (g_spdk_event_mempool[i] == NULL) { + for (j = i - 1; j < i; j--) { + if (g_spdk_event_mempool[j] != NULL) { + spdk_mempool_free(g_spdk_event_mempool[j]); + } + } + SPDK_ERRLOG("spdk_event_mempool creation failed\n"); + return -1; + } + } + } else { + g_spdk_event_mempool[i] = NULL; + } + } + + /* struct spdk_reactor must be aligned on 64 byte boundary */ + last_core = spdk_env_get_last_core(); + rc = posix_memalign((void **)&g_reactors, 64, + (last_core + 1) * sizeof(struct spdk_reactor)); + if (rc != 0) { + SPDK_ERRLOG("Could not allocate array size=%u for g_reactors\n", + last_core + 1); + for (i = 0; i < SPDK_MAX_SOCKET; i++) { + if (g_spdk_event_mempool[i] != NULL) { + spdk_mempool_free(g_spdk_event_mempool[i]); + } + } + return -1; + } + + memset(g_reactors, 0, (last_core + 1) * sizeof(struct spdk_reactor)); + + SPDK_ENV_FOREACH_CORE(i) { + reactor = spdk_reactor_get(i); + spdk_reactor_construct(reactor, i, max_delay_us); + } + + g_reactor_state = SPDK_REACTOR_STATE_INITIALIZED; + + return 0; +} + +void +spdk_reactors_fini(void) +{ + uint32_t i; + struct spdk_reactor *reactor; + + SPDK_ENV_FOREACH_CORE(i) { + reactor = spdk_reactor_get(i); + if (spdk_likely(reactor != NULL) && reactor->events != NULL) { + spdk_ring_free(reactor->events); + } + } + + for (i = 0; i < SPDK_MAX_SOCKET; i++) { + if (g_spdk_event_mempool[i] != NULL) { + spdk_mempool_free(g_spdk_event_mempool[i]); + } + } + + free(g_reactors); + g_reactors = NULL; +} + +SPDK_LOG_REGISTER_COMPONENT("reactor", SPDK_LOG_REACTOR) diff --git a/src/spdk/lib/event/rpc.c b/src/spdk/lib/event/rpc.c new file mode 100644 index 00000000..f8414349 --- /dev/null +++ b/src/spdk/lib/event/rpc.c @@ -0,0 +1,82 @@ +/*- + * 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/conf.h" +#include "spdk/env.h" +#include "spdk/thread.h" +#include "spdk/log.h" +#include "spdk/rpc.h" + +#include "spdk_internal/event.h" + +#define RPC_SELECT_INTERVAL 4000 /* 4ms */ + +static struct spdk_poller *g_rpc_poller = NULL; + +static int +spdk_rpc_subsystem_poll(void *arg) +{ + spdk_rpc_accept(); + return -1; +} + +void +spdk_rpc_initialize(const char *listen_addr) +{ + int rc; + + if (listen_addr == NULL) { + return; + } + + /* Listen on the requested address */ + rc = spdk_rpc_listen(listen_addr); + if (rc != 0) { + SPDK_ERRLOG("Unable to start RPC service at %s\n", listen_addr); + return; + } + + spdk_rpc_set_state(SPDK_RPC_STARTUP); + + /* Register a poller to periodically check for RPCs */ + g_rpc_poller = spdk_poller_register(spdk_rpc_subsystem_poll, NULL, RPC_SELECT_INTERVAL); +} + +void +spdk_rpc_finish(void) +{ + spdk_rpc_close(); + spdk_poller_unregister(&g_rpc_poller); +} diff --git a/src/spdk/lib/event/rpc/Makefile b/src/spdk/lib/event/rpc/Makefile new file mode 100644 index 00000000..fcba526a --- /dev/null +++ b/src/spdk/lib/event/rpc/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = app_rpc.c subsystem_rpc.c +LIBNAME = app_rpc + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/rpc/app_rpc.c b/src/spdk/lib/event/rpc/app_rpc.c new file mode 100644 index 00000000..95cb0d2a --- /dev/null +++ b/src/spdk/lib/event/rpc/app_rpc.c @@ -0,0 +1,155 @@ +/*- + * 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/event.h" +#include "spdk/rpc.h" +#include "spdk/util.h" + +#include "spdk_internal/log.h" + +struct rpc_kill_instance { + char *sig_name; +}; + +static void +free_rpc_kill_instance(struct rpc_kill_instance *req) +{ + free(req->sig_name); +} + +static const struct spdk_json_object_decoder rpc_kill_instance_decoders[] = { + {"sig_name", offsetof(struct rpc_kill_instance, sig_name), spdk_json_decode_string}, +}; + +static void +spdk_rpc_kill_instance(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + static const struct { + const char *signal_string; + int32_t signal; + } signals[] = { + {"SIGINT", SIGINT}, + {"SIGTERM", SIGTERM}, + {"SIGQUIT", SIGQUIT}, + {"SIGHUP", SIGHUP}, + {"SIGKILL", SIGKILL}, + }; + size_t i, sig_count; + int signal; + struct rpc_kill_instance req = {}; + struct spdk_json_write_ctx *w; + + if (spdk_json_decode_object(params, rpc_kill_instance_decoders, + SPDK_COUNTOF(rpc_kill_instance_decoders), + &req)) { + SPDK_DEBUGLOG(SPDK_LOG_REACTOR, "spdk_json_decode_object failed\n"); + goto invalid; + } + + sig_count = SPDK_COUNTOF(signals); + signal = atoi(req.sig_name); + for (i = 0 ; i < sig_count; i++) { + if (strcmp(req.sig_name, signals[i].signal_string) == 0 || + signal == signals[i].signal) { + break; + } + } + + if (i == sig_count) { + goto invalid; + } + + SPDK_DEBUGLOG(SPDK_LOG_REACTOR, "sending signal %d\n", signals[i].signal); + free_rpc_kill_instance(&req); + kill(getpid(), signals[i].signal); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_kill_instance(&req); +} +SPDK_RPC_REGISTER("kill_instance", spdk_rpc_kill_instance, SPDK_RPC_RUNTIME) + + +struct rpc_context_switch_monitor { + bool enabled; +}; + +static const struct spdk_json_object_decoder rpc_context_switch_monitor_decoders[] = { + {"enabled", offsetof(struct rpc_context_switch_monitor, enabled), spdk_json_decode_bool}, +}; + +static void +spdk_rpc_context_switch_monitor(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_context_switch_monitor req = {}; + struct spdk_json_write_ctx *w; + + if (params != NULL) { + if (spdk_json_decode_object(params, rpc_context_switch_monitor_decoders, + SPDK_COUNTOF(rpc_context_switch_monitor_decoders), + &req)) { + SPDK_DEBUGLOG(SPDK_LOG_REACTOR, "spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + return; + } + + spdk_reactor_enable_context_switch_monitor(req.enabled); + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_object_begin(w); + + spdk_json_write_name(w, "enabled"); + spdk_json_write_bool(w, spdk_reactor_context_switch_monitor_enabled()); + + spdk_json_write_object_end(w); + spdk_jsonrpc_end_result(request, w); +} + +SPDK_RPC_REGISTER("context_switch_monitor", spdk_rpc_context_switch_monitor, SPDK_RPC_RUNTIME) diff --git a/src/spdk/lib/event/rpc/subsystem_rpc.c b/src/spdk/lib/event/rpc/subsystem_rpc.c new file mode 100644 index 00000000..1b83990f --- /dev/null +++ b/src/spdk/lib/event/rpc/subsystem_rpc.c @@ -0,0 +1,129 @@ +/*- + * 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_internal/event.h" +#include "spdk/rpc.h" +#include "spdk/string.h" +#include "spdk/util.h" +#include "spdk/env.h" + +static void +spdk_rpc_get_subsystems(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct spdk_json_write_ctx *w; + struct spdk_subsystem *subsystem; + struct spdk_subsystem_depend *deps; + + if (params) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "'get_subsystems' requires no arguments"); + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_array_begin(w); + TAILQ_FOREACH(subsystem, &g_subsystems, tailq) { + spdk_json_write_object_begin(w); + + spdk_json_write_named_string(w, "subsystem", subsystem->name); + spdk_json_write_named_array_begin(w, "depends_on"); + TAILQ_FOREACH(deps, &g_subsystems_deps, tailq) { + if (strcmp(subsystem->name, deps->name) == 0) { + spdk_json_write_string(w, deps->depends_on); + } + } + spdk_json_write_array_end(w); + + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + spdk_jsonrpc_end_result(request, w); +} + +SPDK_RPC_REGISTER("get_subsystems", spdk_rpc_get_subsystems, SPDK_RPC_RUNTIME) + +struct rpc_get_subsystem_config { + char *name; +}; + +static const struct spdk_json_object_decoder rpc_get_subsystem_config[] = { + {"name", offsetof(struct rpc_get_subsystem_config, name), spdk_json_decode_string}, +}; + +static void +rpc_get_subsystem_config_done(void *arg1, void *arg2) +{ + struct spdk_jsonrpc_request *request = arg1; + struct spdk_json_write_ctx *w = arg2; + + spdk_jsonrpc_end_result(request, w); +} + +static void +spdk_rpc_get_subsystem_config(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_get_subsystem_config req = {}; + struct spdk_json_write_ctx *w; + struct spdk_subsystem *subsystem; + struct spdk_event *ev; + + if (spdk_json_decode_object(params, rpc_get_subsystem_config, + SPDK_COUNTOF(rpc_get_subsystem_config), &req)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid arguments"); + return; + } + + subsystem = spdk_subsystem_find(&g_subsystems, req.name); + if (!subsystem) { + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Subsystem '%s' not found", req.name); + free(req.name); + return; + } + + free(req.name); + + w = spdk_jsonrpc_begin_result(request); + if (w) { + ev = spdk_event_allocate(spdk_env_get_current_core(), rpc_get_subsystem_config_done, request, w); + spdk_subsystem_config_json(w, subsystem, ev); + } +} + +SPDK_RPC_REGISTER("get_subsystem_config", spdk_rpc_get_subsystem_config, SPDK_RPC_RUNTIME) diff --git a/src/spdk/lib/event/subsystem.c b/src/spdk/lib/event/subsystem.c new file mode 100644 index 00000000..438e7f54 --- /dev/null +++ b/src/spdk/lib/event/subsystem.c @@ -0,0 +1,256 @@ +/*- + * 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/log.h" + +#include "spdk_internal/event.h" +#include "spdk/env.h" + +struct spdk_subsystem_list g_subsystems = TAILQ_HEAD_INITIALIZER(g_subsystems); +struct spdk_subsystem_depend_list g_subsystems_deps = TAILQ_HEAD_INITIALIZER(g_subsystems_deps); +static struct spdk_subsystem *g_next_subsystem; +static bool g_subsystems_initialized = false; +static struct spdk_event *g_app_start_event; +static struct spdk_event *g_app_stop_event; +static uint32_t g_fini_core; + +void +spdk_add_subsystem(struct spdk_subsystem *subsystem) +{ + TAILQ_INSERT_TAIL(&g_subsystems, subsystem, tailq); +} + +void +spdk_add_subsystem_depend(struct spdk_subsystem_depend *depend) +{ + TAILQ_INSERT_TAIL(&g_subsystems_deps, depend, tailq); +} + +struct spdk_subsystem * +spdk_subsystem_find(struct spdk_subsystem_list *list, const char *name) +{ + struct spdk_subsystem *iter; + + TAILQ_FOREACH(iter, list, tailq) { + if (strcmp(name, iter->name) == 0) { + return iter; + } + } + + return NULL; +} + +static void +subsystem_sort(void) +{ + bool depends_on, depends_on_sorted; + struct spdk_subsystem *subsystem, *subsystem_tmp; + struct spdk_subsystem_depend *subsystem_dep; + + struct spdk_subsystem_list subsystems_list = TAILQ_HEAD_INITIALIZER(subsystems_list); + + while (!TAILQ_EMPTY(&g_subsystems)) { + TAILQ_FOREACH_SAFE(subsystem, &g_subsystems, tailq, subsystem_tmp) { + depends_on = false; + TAILQ_FOREACH(subsystem_dep, &g_subsystems_deps, tailq) { + if (strcmp(subsystem->name, subsystem_dep->name) == 0) { + depends_on = true; + depends_on_sorted = !!spdk_subsystem_find(&subsystems_list, subsystem_dep->depends_on); + if (depends_on_sorted) { + continue; + } + break; + } + } + + if (depends_on == false) { + TAILQ_REMOVE(&g_subsystems, subsystem, tailq); + TAILQ_INSERT_TAIL(&subsystems_list, subsystem, tailq); + } else { + if (depends_on_sorted == true) { + TAILQ_REMOVE(&g_subsystems, subsystem, tailq); + TAILQ_INSERT_TAIL(&subsystems_list, subsystem, tailq); + } + } + } + } + + TAILQ_FOREACH_SAFE(subsystem, &subsystems_list, tailq, subsystem_tmp) { + TAILQ_REMOVE(&subsystems_list, subsystem, tailq); + TAILQ_INSERT_TAIL(&g_subsystems, subsystem, tailq); + } +} + +void +spdk_subsystem_init_next(int rc) +{ + if (rc) { + SPDK_ERRLOG("Init subsystem %s failed\n", g_next_subsystem->name); + spdk_app_stop(rc); + return; + } + + if (!g_next_subsystem) { + g_next_subsystem = TAILQ_FIRST(&g_subsystems); + } else { + g_next_subsystem = TAILQ_NEXT(g_next_subsystem, tailq); + } + + if (!g_next_subsystem) { + g_subsystems_initialized = true; + spdk_event_call(g_app_start_event); + return; + } + + if (g_next_subsystem->init) { + g_next_subsystem->init(); + } else { + spdk_subsystem_init_next(0); + } +} + +static void +spdk_subsystem_verify(void *arg1, void *arg2) +{ + struct spdk_subsystem_depend *dep; + + /* Verify that all dependency name and depends_on subsystems are registered */ + TAILQ_FOREACH(dep, &g_subsystems_deps, tailq) { + if (!spdk_subsystem_find(&g_subsystems, dep->name)) { + SPDK_ERRLOG("subsystem %s is missing\n", dep->name); + spdk_app_stop(-1); + return; + } + if (!spdk_subsystem_find(&g_subsystems, dep->depends_on)) { + SPDK_ERRLOG("subsystem %s dependency %s is missing\n", + dep->name, dep->depends_on); + spdk_app_stop(-1); + return; + } + } + + subsystem_sort(); + + spdk_subsystem_init_next(0); +} + +void +spdk_subsystem_init(struct spdk_event *app_start_event) +{ + struct spdk_event *verify_event; + + g_app_start_event = app_start_event; + + verify_event = spdk_event_allocate(spdk_env_get_current_core(), spdk_subsystem_verify, NULL, NULL); + spdk_event_call(verify_event); +} + +static void +_spdk_subsystem_fini_next(void *arg1, void *arg2) +{ + assert(g_fini_core == spdk_env_get_current_core()); + + if (!g_next_subsystem) { + /* If the initialized flag is false, then we've failed to initialize + * the very first subsystem and no de-init is needed + */ + if (g_subsystems_initialized) { + g_next_subsystem = TAILQ_LAST(&g_subsystems, spdk_subsystem_list); + } + } else { + /* We rewind the g_next_subsystem unconditionally - even when some subsystem failed + * to initialize. It is assumed that subsystem which failed to initialize does not + * need to be deinitialized. + */ + g_next_subsystem = TAILQ_PREV(g_next_subsystem, spdk_subsystem_list, tailq); + } + + while (g_next_subsystem) { + if (g_next_subsystem->fini) { + g_next_subsystem->fini(); + return; + } + g_next_subsystem = TAILQ_PREV(g_next_subsystem, spdk_subsystem_list, tailq); + } + + spdk_event_call(g_app_stop_event); + return; +} + +void +spdk_subsystem_fini_next(void) +{ + if (g_fini_core != spdk_env_get_current_core()) { + struct spdk_event *event; + + event = spdk_event_allocate(g_fini_core, _spdk_subsystem_fini_next, NULL, NULL); + spdk_event_call(event); + } else { + _spdk_subsystem_fini_next(NULL, NULL); + } +} + +void +spdk_subsystem_fini(struct spdk_event *app_stop_event) +{ + g_app_stop_event = app_stop_event; + g_fini_core = spdk_env_get_current_core(); + + spdk_subsystem_fini_next(); +} + +void +spdk_subsystem_config(FILE *fp) +{ + struct spdk_subsystem *subsystem; + + TAILQ_FOREACH(subsystem, &g_subsystems, tailq) { + if (subsystem->config) { + subsystem->config(fp); + } + } +} + +void +spdk_subsystem_config_json(struct spdk_json_write_ctx *w, struct spdk_subsystem *subsystem, + struct spdk_event *done_ev) +{ + if (subsystem && subsystem->write_config_json) { + subsystem->write_config_json(w, done_ev); + } else { + spdk_json_write_null(w); + spdk_event_call(done_ev); + } +} diff --git a/src/spdk/lib/event/subsystems/Makefile b/src/spdk/lib/event/subsystems/Makefile new file mode 100644 index 00000000..4a19160b --- /dev/null +++ b/src/spdk/lib/event/subsystems/Makefile @@ -0,0 +1,44 @@ +# +# 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 += bdev copy iscsi nbd net nvmf scsi vhost + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/src/spdk/lib/event/subsystems/bdev/Makefile b/src/spdk/lib/event/subsystems/bdev/Makefile new file mode 100644 index 00000000..1747b759 --- /dev/null +++ b/src/spdk/lib/event/subsystems/bdev/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = bdev.c bdev_rpc.c +LIBNAME = event_bdev + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/bdev/bdev.c b/src/spdk/lib/event/subsystems/bdev/bdev.c new file mode 100644 index 00000000..5999d612 --- /dev/null +++ b/src/spdk/lib/event/subsystems/bdev/bdev.c @@ -0,0 +1,83 @@ +/*- + * 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/bdev.h" +#include "spdk/env.h" +#include "spdk/thread.h" + +#include "spdk_internal/event.h" +#include "spdk/env.h" + +static void +spdk_bdev_initialize_complete(void *cb_arg, int rc) +{ + spdk_subsystem_init_next(rc); +} + +static void +spdk_bdev_subsystem_initialize(void) +{ + spdk_bdev_initialize(spdk_bdev_initialize_complete, NULL); +} + +static void +spdk_bdev_subsystem_finish_done(void *cb_arg) +{ + spdk_subsystem_fini_next(); +} + +static void +spdk_bdev_subsystem_finish(void) +{ + spdk_bdev_finish(spdk_bdev_subsystem_finish_done, NULL); +} + +static void +_spdk_bdev_subsystem_config_json(struct spdk_json_write_ctx *w, struct spdk_event *done_ev) +{ + spdk_bdev_subsystem_config_json(w); + spdk_event_call(done_ev); +} + +static struct spdk_subsystem g_spdk_subsystem_bdev = { + .name = "bdev", + .init = spdk_bdev_subsystem_initialize, + .fini = spdk_bdev_subsystem_finish, + .config = spdk_bdev_config_text, + .write_config_json = _spdk_bdev_subsystem_config_json, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_bdev); +SPDK_SUBSYSTEM_DEPEND(bdev, copy) diff --git a/src/spdk/lib/event/subsystems/bdev/bdev_rpc.c b/src/spdk/lib/event/subsystems/bdev/bdev_rpc.c new file mode 100644 index 00000000..69ead5f2 --- /dev/null +++ b/src/spdk/lib/event/subsystems/bdev/bdev_rpc.c @@ -0,0 +1,97 @@ +/*- + * 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/bdev.h" + +#include "spdk/rpc.h" +#include "spdk/util.h" +#include "spdk/string.h" + +#include "spdk_internal/log.h" + +struct spdk_rpc_set_bdev_opts { + uint32_t bdev_io_pool_size; + uint32_t bdev_io_cache_size; +}; + +static const struct spdk_json_object_decoder rpc_set_bdev_opts_decoders[] = { + {"bdev_io_pool_size", offsetof(struct spdk_rpc_set_bdev_opts, bdev_io_pool_size), spdk_json_decode_uint32, true}, + {"bdev_io_cache_size", offsetof(struct spdk_rpc_set_bdev_opts, bdev_io_cache_size), spdk_json_decode_uint32, true}, +}; + +static void +spdk_rpc_set_bdev_opts(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) +{ + struct spdk_rpc_set_bdev_opts rpc_opts; + struct spdk_bdev_opts bdev_opts; + struct spdk_json_write_ctx *w; + int rc; + + rpc_opts.bdev_io_pool_size = UINT32_MAX; + rpc_opts.bdev_io_cache_size = UINT32_MAX; + + if (params != NULL) { + if (spdk_json_decode_object(params, rpc_set_bdev_opts_decoders, + SPDK_COUNTOF(rpc_set_bdev_opts_decoders), &rpc_opts)) { + SPDK_ERRLOG("spdk_json_decode_object() failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + return; + } + } + + spdk_bdev_get_opts(&bdev_opts); + if (rpc_opts.bdev_io_pool_size != UINT32_MAX) { + bdev_opts.bdev_io_pool_size = rpc_opts.bdev_io_pool_size; + } + if (rpc_opts.bdev_io_cache_size != UINT32_MAX) { + bdev_opts.bdev_io_cache_size = rpc_opts.bdev_io_cache_size; + } + rc = spdk_bdev_set_opts(&bdev_opts); + + if (rc != 0) { + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Pool size %" PRIu32 " too small for cache size %" PRIu32, + bdev_opts.bdev_io_pool_size, bdev_opts.bdev_io_cache_size); + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("set_bdev_options", spdk_rpc_set_bdev_opts, SPDK_RPC_STARTUP) diff --git a/src/spdk/lib/event/subsystems/copy/Makefile b/src/spdk/lib/event/subsystems/copy/Makefile new file mode 100644 index 00000000..691eee2a --- /dev/null +++ b/src/spdk/lib/event/subsystems/copy/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = copy.c +LIBNAME = event_copy + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/copy/copy.c b/src/spdk/lib/event/subsystems/copy/copy.c new file mode 100644 index 00000000..9bc6e281 --- /dev/null +++ b/src/spdk/lib/event/subsystems/copy/copy.c @@ -0,0 +1,70 @@ +/*- + * 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/copy_engine.h" + +#include "spdk_internal/event.h" +#include "spdk/env.h" + +static void +spdk_copy_engine_subsystem_initialize(void) +{ + int rc; + + rc = spdk_copy_engine_initialize(); + + spdk_subsystem_init_next(rc); +} + +static void +spdk_copy_engine_subsystem_finish_done(void *cb_arg) +{ + spdk_subsystem_fini_next(); +} + +static void +spdk_copy_engine_subsystem_finish(void) +{ + spdk_copy_engine_finish(spdk_copy_engine_subsystem_finish_done, NULL); +} + +static struct spdk_subsystem g_spdk_subsystem_copy = { + .name = "copy", + .init = spdk_copy_engine_subsystem_initialize, + .fini = spdk_copy_engine_subsystem_finish, + .config = spdk_copy_engine_config_text, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_copy); diff --git a/src/spdk/lib/event/subsystems/iscsi/Makefile b/src/spdk/lib/event/subsystems/iscsi/Makefile new file mode 100644 index 00000000..f57d9f9c --- /dev/null +++ b/src/spdk/lib/event/subsystems/iscsi/Makefile @@ -0,0 +1,41 @@ +# +# 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 + +CFLAGS += -I$(SPDK_ROOT_DIR)/lib +C_SRCS = iscsi.c iscsi_rpc.c +LIBNAME = event_iscsi + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/iscsi/iscsi.c b/src/spdk/lib/event/subsystems/iscsi/iscsi.c new file mode 100644 index 00000000..72750398 --- /dev/null +++ b/src/spdk/lib/event/subsystems/iscsi/iscsi.c @@ -0,0 +1,81 @@ +/*- + * 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 "iscsi/iscsi.h" + +#include "spdk_internal/event.h" + +static void +spdk_iscsi_subsystem_init_complete(void *cb_arg, int rc) +{ + spdk_subsystem_init_next(rc); +} + +static void +spdk_iscsi_subsystem_init(void) +{ + spdk_iscsi_init(spdk_iscsi_subsystem_init_complete, NULL); +} + +static void +spdk_iscsi_subsystem_fini_done(void *arg) +{ + spdk_subsystem_fini_next(); +} + +static void +spdk_iscsi_subsystem_fini(void) +{ + spdk_iscsi_fini(spdk_iscsi_subsystem_fini_done, NULL); +} + +static void +spdk_iscsi_subsystem_config_json(struct spdk_json_write_ctx *w, + struct spdk_event *done_ev) +{ + spdk_iscsi_config_json(w); + spdk_event_call(done_ev); +} + +static struct spdk_subsystem g_spdk_subsystem_iscsi = { + .name = "iscsi", + .init = spdk_iscsi_subsystem_init, + .fini = spdk_iscsi_subsystem_fini, + .config = spdk_iscsi_config_text, + .write_config_json = spdk_iscsi_subsystem_config_json, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_iscsi); +SPDK_SUBSYSTEM_DEPEND(iscsi, scsi) diff --git a/src/spdk/lib/event/subsystems/iscsi/iscsi_rpc.c b/src/spdk/lib/event/subsystems/iscsi/iscsi_rpc.c new file mode 100644 index 00000000..fb96be07 --- /dev/null +++ b/src/spdk/lib/event/subsystems/iscsi/iscsi_rpc.c @@ -0,0 +1,119 @@ +/*- + * 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 "iscsi/iscsi.h" +#include "iscsi/conn.h" + +#include "spdk/rpc.h" +#include "spdk/util.h" +#include "spdk/event.h" + +#include "spdk_internal/log.h" + +static const struct spdk_json_object_decoder rpc_set_iscsi_opts_decoders[] = { + {"auth_file", offsetof(struct spdk_iscsi_opts, authfile), spdk_json_decode_string, true}, + {"node_base", offsetof(struct spdk_iscsi_opts, nodebase), spdk_json_decode_string, true}, + {"nop_timeout", offsetof(struct spdk_iscsi_opts, timeout), spdk_json_decode_int32, true}, + {"nop_in_interval", offsetof(struct spdk_iscsi_opts, nopininterval), spdk_json_decode_int32, true}, + {"no_discovery_auth", offsetof(struct spdk_iscsi_opts, disable_chap), spdk_json_decode_bool, true}, + {"req_discovery_auth", offsetof(struct spdk_iscsi_opts, require_chap), spdk_json_decode_bool, true}, + {"req_discovery_auth_mutual", offsetof(struct spdk_iscsi_opts, mutual_chap), spdk_json_decode_bool, true}, + {"discovery_auth_group", offsetof(struct spdk_iscsi_opts, chap_group), spdk_json_decode_int32, true}, + {"disable_chap", offsetof(struct spdk_iscsi_opts, disable_chap), spdk_json_decode_bool, true}, + {"require_chap", offsetof(struct spdk_iscsi_opts, require_chap), spdk_json_decode_bool, true}, + {"mutual_chap", offsetof(struct spdk_iscsi_opts, mutual_chap), spdk_json_decode_bool, true}, + {"chap_group", offsetof(struct spdk_iscsi_opts, chap_group), spdk_json_decode_int32, true}, + {"max_sessions", offsetof(struct spdk_iscsi_opts, MaxSessions), spdk_json_decode_uint32, true}, + {"max_queue_depth", offsetof(struct spdk_iscsi_opts, MaxQueueDepth), spdk_json_decode_uint32, true}, + {"max_connections_per_session", offsetof(struct spdk_iscsi_opts, MaxConnectionsPerSession), spdk_json_decode_uint32, true}, + {"default_time2wait", offsetof(struct spdk_iscsi_opts, DefaultTime2Wait), spdk_json_decode_uint32, true}, + {"default_time2retain", offsetof(struct spdk_iscsi_opts, DefaultTime2Retain), spdk_json_decode_uint32, true}, + {"first_burst_length", offsetof(struct spdk_iscsi_opts, FirstBurstLength), spdk_json_decode_uint32, true}, + {"immediate_data", offsetof(struct spdk_iscsi_opts, ImmediateData), spdk_json_decode_bool, true}, + {"error_recovery_level", offsetof(struct spdk_iscsi_opts, ErrorRecoveryLevel), spdk_json_decode_uint32, true}, + {"allow_duplicated_isid", offsetof(struct spdk_iscsi_opts, AllowDuplicateIsid), spdk_json_decode_bool, true}, + {"min_connections_per_core", offsetof(struct spdk_iscsi_opts, min_connections_per_core), spdk_json_decode_uint32, true}, +}; + +static void +spdk_rpc_iscsi_set_opts(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct spdk_iscsi_opts *opts; + struct spdk_json_write_ctx *w; + + if (g_spdk_iscsi_opts != NULL) { + SPDK_ERRLOG("this RPC must not be called more than once.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Must not call more than once"); + return; + } + + opts = spdk_iscsi_opts_alloc(); + if (opts == NULL) { + SPDK_ERRLOG("spdk_iscsi_opts_alloc() failed.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Out of memory"); + return; + } + + if (params != NULL) { + if (spdk_json_decode_object(params, rpc_set_iscsi_opts_decoders, + SPDK_COUNTOF(rpc_set_iscsi_opts_decoders), opts)) { + SPDK_ERRLOG("spdk_json_decode_object() failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + spdk_iscsi_opts_free(opts); + return; + } + } + + g_spdk_iscsi_opts = spdk_iscsi_opts_copy(opts); + spdk_iscsi_opts_free(opts); + + if (g_spdk_iscsi_opts == NULL) { + SPDK_ERRLOG("spdk_iscsi_opts_copy() failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Out of memory"); + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("set_iscsi_options", spdk_rpc_iscsi_set_opts, SPDK_RPC_STARTUP) diff --git a/src/spdk/lib/event/subsystems/nbd/Makefile b/src/spdk/lib/event/subsystems/nbd/Makefile new file mode 100644 index 00000000..92d99f15 --- /dev/null +++ b/src/spdk/lib/event/subsystems/nbd/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = nbd.c +LIBNAME = event_nbd + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/nbd/nbd.c b/src/spdk/lib/event/subsystems/nbd/nbd.c new file mode 100644 index 00000000..a943eb82 --- /dev/null +++ b/src/spdk/lib/event/subsystems/nbd/nbd.c @@ -0,0 +1,74 @@ +/*- + * 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/nbd.h" + +#include "spdk_internal/event.h" + +static void +spdk_nbd_subsystem_init(void) +{ + int rc; + + rc = spdk_nbd_init(); + + spdk_subsystem_init_next(rc); +} + +static void +spdk_nbd_subsystem_fini(void) +{ + spdk_nbd_fini(); + spdk_subsystem_fini_next(); +} + +static void +spdk_nbd_subsystem_write_config_json(struct spdk_json_write_ctx *w, + struct spdk_event *done_ev) +{ + spdk_nbd_write_config_json(w); + spdk_event_call(done_ev); +} + +static struct spdk_subsystem g_spdk_subsystem_nbd = { + .name = "nbd", + .init = spdk_nbd_subsystem_init, + .fini = spdk_nbd_subsystem_fini, + .config = NULL, + .write_config_json = spdk_nbd_subsystem_write_config_json, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_nbd); +SPDK_SUBSYSTEM_DEPEND(nbd, bdev) diff --git a/src/spdk/lib/event/subsystems/net/Makefile b/src/spdk/lib/event/subsystems/net/Makefile new file mode 100644 index 00000000..cf81f07b --- /dev/null +++ b/src/spdk/lib/event/subsystems/net/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = net.c +LIBNAME = event_net + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/net/net.c b/src/spdk/lib/event/subsystems/net/net.c new file mode 100644 index 00000000..9355514f --- /dev/null +++ b/src/spdk/lib/event/subsystems/net/net.c @@ -0,0 +1,91 @@ +/*- + * 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/net.h" + +#include "spdk_internal/event.h" + +static void +spdk_interface_subsystem_init(void) +{ + int rc; + + rc = spdk_interface_init(); + + spdk_subsystem_init_next(rc); +} + +static void +spdk_interface_subsystem_destroy(void) +{ + spdk_interface_destroy(); + spdk_subsystem_fini_next(); +} + +static struct spdk_subsystem g_spdk_subsystem_interface = { + .name = "interface", + .init = spdk_interface_subsystem_init, + .fini = spdk_interface_subsystem_destroy, + .config = NULL, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_interface); + +static void +spdk_net_subsystem_start(void) +{ + int rc; + + rc = spdk_net_framework_start(); + + spdk_subsystem_init_next(rc); +} + +static void +spdk_net_subsystem_fini(void) +{ + spdk_net_framework_fini(); + spdk_subsystem_fini_next(); +} + +static struct spdk_subsystem g_spdk_subsystem_net_framework = { + .name = "net_framework", + .init = spdk_net_subsystem_start, + .fini = spdk_net_subsystem_fini, + .config = NULL, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_net_framework); +SPDK_SUBSYSTEM_DEPEND(net_framework, interface) diff --git a/src/spdk/lib/event/subsystems/nvmf/Makefile b/src/spdk/lib/event/subsystems/nvmf/Makefile new file mode 100644 index 00000000..eca62e25 --- /dev/null +++ b/src/spdk/lib/event/subsystems/nvmf/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = conf.c nvmf_rpc.c nvmf_rpc_deprecated.c nvmf_tgt.c +LIBNAME = event_nvmf + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/nvmf/conf.c b/src/spdk/lib/event/subsystems/nvmf/conf.c new file mode 100644 index 00000000..986e81c9 --- /dev/null +++ b/src/spdk/lib/event/subsystems/nvmf/conf.c @@ -0,0 +1,587 @@ +/*- + * 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 "event_nvmf.h" + +#include "spdk/conf.h" +#include "spdk/log.h" +#include "spdk/bdev.h" +#include "spdk/nvme.h" +#include "spdk/nvmf.h" +#include "spdk/string.h" +#include "spdk/util.h" + +#define SPDK_NVMF_MAX_NAMESPACES (1 << 14) + +struct spdk_nvmf_tgt_opts *g_spdk_nvmf_tgt_opts = NULL; +struct spdk_nvmf_tgt_conf *g_spdk_nvmf_tgt_conf = NULL; + +static int +spdk_add_nvmf_discovery_subsystem(void) +{ + struct spdk_nvmf_subsystem *subsystem; + + subsystem = spdk_nvmf_subsystem_create(g_spdk_nvmf_tgt, SPDK_NVMF_DISCOVERY_NQN, + SPDK_NVMF_SUBTYPE_DISCOVERY, 0); + if (subsystem == NULL) { + SPDK_ERRLOG("Failed creating discovery nvmf library subsystem\n"); + return -1; + } + + spdk_nvmf_subsystem_set_allow_any_host(subsystem, true); + + return 0; +} + +static void +spdk_nvmf_read_config_file_tgt_opts(struct spdk_conf_section *sp, + struct spdk_nvmf_tgt_opts *opts) +{ + int max_queue_depth; + int max_queues_per_sess; + int in_capsule_data_size; + int max_io_size; + int io_unit_size; + + max_queue_depth = spdk_conf_section_get_intval(sp, "MaxQueueDepth"); + if (max_queue_depth >= 0) { + opts->max_queue_depth = max_queue_depth; + } + + max_queues_per_sess = spdk_conf_section_get_intval(sp, "MaxQueuesPerSession"); + if (max_queues_per_sess >= 0) { + opts->max_qpairs_per_ctrlr = max_queues_per_sess; + } + + in_capsule_data_size = spdk_conf_section_get_intval(sp, "InCapsuleDataSize"); + if (in_capsule_data_size >= 0) { + opts->in_capsule_data_size = in_capsule_data_size; + } + + max_io_size = spdk_conf_section_get_intval(sp, "MaxIOSize"); + if (max_io_size >= 0) { + opts->max_io_size = max_io_size; + } + + io_unit_size = spdk_conf_section_get_intval(sp, "IOUnitSize"); + if (io_unit_size >= 0) { + opts->io_unit_size = io_unit_size; + } +} + +static void +spdk_nvmf_read_config_file_tgt_conf(struct spdk_conf_section *sp, + struct spdk_nvmf_tgt_conf *conf) +{ + int acceptor_poll_rate; + + acceptor_poll_rate = spdk_conf_section_get_intval(sp, "AcceptorPollRate"); + if (acceptor_poll_rate >= 0) { + conf->acceptor_poll_rate = acceptor_poll_rate; + } +} + +static struct spdk_nvmf_tgt_opts * +spdk_nvmf_parse_tgt_opts(void) +{ + struct spdk_nvmf_tgt_opts *opts; + struct spdk_conf_section *sp; + + opts = calloc(1, sizeof(*opts)); + if (!opts) { + SPDK_ERRLOG("calloc() failed for target options\n"); + return NULL; + } + + spdk_nvmf_tgt_opts_init(opts); + + sp = spdk_conf_find_section(NULL, "Nvmf"); + if (sp != NULL) { + spdk_nvmf_read_config_file_tgt_opts(sp, opts); + } + + return opts; +} + +static struct spdk_nvmf_tgt_conf * +spdk_nvmf_parse_tgt_conf(void) +{ + struct spdk_nvmf_tgt_conf *conf; + struct spdk_conf_section *sp; + + conf = calloc(1, sizeof(*conf)); + if (!conf) { + SPDK_ERRLOG("calloc() failed for target conf\n"); + return NULL; + } + + conf->acceptor_poll_rate = ACCEPT_TIMEOUT_US; + conf->conn_sched = DEFAULT_CONN_SCHED; + + sp = spdk_conf_find_section(NULL, "Nvmf"); + if (sp != NULL) { + spdk_nvmf_read_config_file_tgt_conf(sp, conf); + } + + return conf; +} + +static int +spdk_nvmf_parse_nvmf_tgt(void) +{ + int rc; + + if (!g_spdk_nvmf_tgt_opts) { + g_spdk_nvmf_tgt_opts = spdk_nvmf_parse_tgt_opts(); + if (!g_spdk_nvmf_tgt_opts) { + SPDK_ERRLOG("spdk_nvmf_parse_tgt_opts() failed\n"); + return -1; + } + } + + if (!g_spdk_nvmf_tgt_conf) { + g_spdk_nvmf_tgt_conf = spdk_nvmf_parse_tgt_conf(); + if (!g_spdk_nvmf_tgt_conf) { + SPDK_ERRLOG("spdk_nvmf_parse_tgt_conf() failed\n"); + return -1; + } + } + + g_spdk_nvmf_tgt = spdk_nvmf_tgt_create(g_spdk_nvmf_tgt_opts); + + free(g_spdk_nvmf_tgt_opts); + g_spdk_nvmf_tgt_opts = NULL; + + if (!g_spdk_nvmf_tgt) { + SPDK_ERRLOG("spdk_nvmf_tgt_create() failed\n"); + return -1; + } + + rc = spdk_add_nvmf_discovery_subsystem(); + if (rc != 0) { + SPDK_ERRLOG("spdk_add_nvmf_discovery_subsystem failed\n"); + return rc; + } + + return 0; +} + +static void +spdk_nvmf_tgt_listen_done(void *cb_arg, int status) +{ + /* TODO: Config parsing should wait for this operation to finish. */ + + if (status) { + SPDK_ERRLOG("Failed to listen on transport address\n"); + } +} + +static int +spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp) +{ + const char *nqn, *mode; + size_t i; + int ret; + int lcore; + bool allow_any_host; + const char *sn; + struct spdk_nvmf_subsystem *subsystem; + int num_ns; + + nqn = spdk_conf_section_get_val(sp, "NQN"); + if (nqn == NULL) { + SPDK_ERRLOG("Subsystem missing NQN\n"); + return -1; + } + + mode = spdk_conf_section_get_val(sp, "Mode"); + lcore = spdk_conf_section_get_intval(sp, "Core"); + num_ns = spdk_conf_section_get_intval(sp, "MaxNamespaces"); + + if (num_ns < 1) { + num_ns = 0; + } else if (num_ns > SPDK_NVMF_MAX_NAMESPACES) { + num_ns = SPDK_NVMF_MAX_NAMESPACES; + } + + /* Mode is no longer a valid parameter, but print out a nice + * message if it exists to inform users. + */ + if (mode) { + SPDK_NOTICELOG("Mode present in the [Subsystem] section of the config file.\n" + "Mode was removed as a valid parameter.\n"); + if (strcasecmp(mode, "Virtual") == 0) { + SPDK_NOTICELOG("Your mode value is 'Virtual' which is now the only possible mode.\n" + "Your configuration file will work as expected.\n"); + } else { + SPDK_NOTICELOG("Please remove Mode from your configuration file.\n"); + return -1; + } + } + + /* Core is no longer a valid parameter, but print out a nice + * message if it exists to inform users. + */ + if (lcore >= 0) { + SPDK_NOTICELOG("Core present in the [Subsystem] section of the config file.\n" + "Core was removed as an option. Subsystems can now run on all available cores.\n"); + SPDK_NOTICELOG("Please remove Core from your configuration file. Ignoring it and continuing.\n"); + } + + sn = spdk_conf_section_get_val(sp, "SN"); + if (sn == NULL) { + SPDK_ERRLOG("Subsystem %s: missing serial number\n", nqn); + return -1; + } + + subsystem = spdk_nvmf_subsystem_create(g_spdk_nvmf_tgt, nqn, SPDK_NVMF_SUBTYPE_NVME, num_ns); + if (subsystem == NULL) { + goto done; + } + + if (spdk_nvmf_subsystem_set_sn(subsystem, sn)) { + SPDK_ERRLOG("Subsystem %s: invalid serial number '%s'\n", nqn, sn); + spdk_nvmf_subsystem_destroy(subsystem); + subsystem = NULL; + goto done; + } + + for (i = 0; ; i++) { + struct spdk_nvmf_ns_opts ns_opts; + struct spdk_bdev *bdev; + const char *bdev_name; + const char *uuid_str; + char *nsid_str; + + bdev_name = spdk_conf_section_get_nmval(sp, "Namespace", i, 0); + if (!bdev_name) { + break; + } + + bdev = spdk_bdev_get_by_name(bdev_name); + if (bdev == NULL) { + SPDK_ERRLOG("Could not find namespace bdev '%s'\n", bdev_name); + spdk_nvmf_subsystem_destroy(subsystem); + subsystem = NULL; + goto done; + } + + spdk_nvmf_ns_opts_get_defaults(&ns_opts, sizeof(ns_opts)); + + nsid_str = spdk_conf_section_get_nmval(sp, "Namespace", i, 1); + if (nsid_str) { + char *end; + unsigned long nsid_ul = strtoul(nsid_str, &end, 0); + + if (*end != '\0' || nsid_ul == 0 || nsid_ul >= UINT32_MAX) { + SPDK_ERRLOG("Invalid NSID %s\n", nsid_str); + spdk_nvmf_subsystem_destroy(subsystem); + subsystem = NULL; + goto done; + } + + ns_opts.nsid = (uint32_t)nsid_ul; + } + + uuid_str = spdk_conf_section_get_nmval(sp, "Namespace", i, 2); + if (uuid_str) { + if (spdk_uuid_parse(&ns_opts.uuid, uuid_str)) { + SPDK_ERRLOG("Invalid UUID %s\n", uuid_str); + spdk_nvmf_subsystem_destroy(subsystem); + subsystem = NULL; + goto done; + } + } + + if (spdk_nvmf_subsystem_add_ns(subsystem, bdev, &ns_opts, sizeof(ns_opts)) == 0) { + SPDK_ERRLOG("Unable to add namespace\n"); + spdk_nvmf_subsystem_destroy(subsystem); + subsystem = NULL; + goto done; + } + + SPDK_INFOLOG(SPDK_LOG_NVMF, "Attaching block device %s to subsystem %s\n", + spdk_bdev_get_name(bdev), spdk_nvmf_subsystem_get_nqn(subsystem)); + } + + /* Parse Listen sections */ + for (i = 0; ; i++) { + struct spdk_nvme_transport_id trid = {0}; + const char *transport; + const char *address; + char *address_dup; + char *host; + char *port; + + transport = spdk_conf_section_get_nmval(sp, "Listen", i, 0); + if (!transport) { + break; + } + + if (spdk_nvme_transport_id_parse_trtype(&trid.trtype, transport)) { + SPDK_ERRLOG("Invalid listen address transport type '%s'\n", transport); + continue; + } + + address = spdk_conf_section_get_nmval(sp, "Listen", i, 1); + if (!address) { + break; + } + + address_dup = strdup(address); + if (!address_dup) { + break; + } + + ret = spdk_parse_ip_addr(address_dup, &host, &port); + if (ret < 0) { + SPDK_ERRLOG("Unable to parse listen address '%s'\n", address); + free(address_dup); + continue; + } + + if (strchr(host, ':')) { + trid.adrfam = SPDK_NVMF_ADRFAM_IPV6; + } else { + trid.adrfam = SPDK_NVMF_ADRFAM_IPV4; + } + + snprintf(trid.traddr, sizeof(trid.traddr), "%s", host); + if (port) { + snprintf(trid.trsvcid, sizeof(trid.trsvcid), "%s", port); + } + free(address_dup); + + spdk_nvmf_tgt_listen(g_spdk_nvmf_tgt, &trid, spdk_nvmf_tgt_listen_done, NULL); + + spdk_nvmf_subsystem_add_listener(subsystem, &trid); + } + + /* Parse Host sections */ + for (i = 0; ; i++) { + const char *host = spdk_conf_section_get_nval(sp, "Host", i); + + if (!host) { + break; + } + + spdk_nvmf_subsystem_add_host(subsystem, host); + } + + allow_any_host = spdk_conf_section_get_boolval(sp, "AllowAnyHost", false); + spdk_nvmf_subsystem_set_allow_any_host(subsystem, allow_any_host); + +done: + return (subsystem != NULL); +} + +static int +spdk_nvmf_parse_subsystems(void) +{ + int rc = 0; + struct spdk_conf_section *sp; + + sp = spdk_conf_first_section(NULL); + while (sp != NULL) { + if (spdk_conf_section_match_prefix(sp, "Subsystem")) { + rc = spdk_nvmf_parse_subsystem(sp); + if (rc < 0) { + return -1; + } + } + sp = spdk_conf_next_section(sp); + } + return 0; +} + +struct spdk_nvmf_parse_transport_ctx { + struct spdk_conf_section *sp; + spdk_nvmf_parse_conf_done_fn cb_fn; +}; + +static void spdk_nvmf_parse_transport(struct spdk_nvmf_parse_transport_ctx *ctx); + +static void +spdk_nvmf_tgt_add_transport_done(void *cb_arg, int status) +{ + struct spdk_nvmf_parse_transport_ctx *ctx = cb_arg; + int rc; + + if (status < 0) { + SPDK_ERRLOG("Add transport to target failed (%d).\n", status); + ctx->cb_fn(status); + free(ctx); + return; + } + + /* find next transport */ + ctx->sp = spdk_conf_next_section(ctx->sp); + while (ctx->sp) { + if (spdk_conf_section_match_prefix(ctx->sp, "Transport")) { + spdk_nvmf_parse_transport(ctx); + return; + } + ctx->sp = spdk_conf_next_section(ctx->sp); + } + + /* done with transports, parse Subsystem sections */ + rc = spdk_nvmf_parse_subsystems(); + + ctx->cb_fn(rc); + free(ctx); +} + +static void +spdk_nvmf_parse_transport(struct spdk_nvmf_parse_transport_ctx *ctx) +{ + const char *type; + struct spdk_nvmf_transport_opts opts = { 0 }; + enum spdk_nvme_transport_type trtype; + struct spdk_nvmf_transport *transport; + int val; + + type = spdk_conf_section_get_val(ctx->sp, "Type"); + if (type == NULL) { + SPDK_ERRLOG("Transport missing Type\n"); + ctx->cb_fn(-1); + free(ctx); + return; + } + + if (spdk_nvme_transport_id_parse_trtype(&trtype, type)) { + SPDK_ERRLOG("Invalid transport type '%s'\n", type); + ctx->cb_fn(-1); + free(ctx); + return; + } + + if (spdk_nvmf_tgt_get_transport(g_spdk_nvmf_tgt, trtype)) { + SPDK_ERRLOG("Duplicate transport type '%s'\n", type); + ctx->cb_fn(-1); + free(ctx); + return; + } + + if (!spdk_nvmf_transport_opts_init(trtype, &opts)) { + ctx->cb_fn(-1); + free(ctx); + return; + } + + val = spdk_conf_section_get_intval(ctx->sp, "MaxQueueDepth"); + if (val >= 0) { + opts.max_queue_depth = val; + } + val = spdk_conf_section_get_intval(ctx->sp, "MaxQueuesPerSession"); + if (val >= 0) { + opts.max_qpairs_per_ctrlr = val; + } + val = spdk_conf_section_get_intval(ctx->sp, "InCapsuleDataSize"); + if (val >= 0) { + opts.in_capsule_data_size = val; + } + val = spdk_conf_section_get_intval(ctx->sp, "MaxIOSize"); + if (val >= 0) { + opts.max_io_size = val; + } + val = spdk_conf_section_get_intval(ctx->sp, "IOUnitSize"); + if (val >= 0) { + opts.io_unit_size = val; + } + val = spdk_conf_section_get_intval(ctx->sp, "MaxAQDepth"); + if (val >= 0) { + opts.max_aq_depth = val; + } + + transport = spdk_nvmf_transport_create(trtype, &opts); + if (transport) { + spdk_nvmf_tgt_add_transport(g_spdk_nvmf_tgt, transport, spdk_nvmf_tgt_add_transport_done, ctx); + } else { + ctx->cb_fn(-1); + free(ctx); + return; + } +} + +static int +spdk_nvmf_parse_transports(spdk_nvmf_parse_conf_done_fn cb_fn) +{ + struct spdk_nvmf_parse_transport_ctx *ctx; + + ctx = calloc(1, sizeof(struct spdk_nvmf_parse_transport_ctx)); + if (!ctx) { + SPDK_ERRLOG("Failed alloc of context memory for parse transports\n"); + return -ENOMEM; + } + + ctx->cb_fn = cb_fn; + ctx->sp = spdk_conf_first_section(NULL); + while (ctx->sp != NULL) { + if (spdk_conf_section_match_prefix(ctx->sp, "Transport")) { + spdk_nvmf_parse_transport(ctx); + return 0; + } + ctx->sp = spdk_conf_next_section(ctx->sp); + } + + /* if we get here, there are no transports defined in conf file */ + free(ctx); + cb_fn(spdk_nvmf_parse_subsystems()); + + return 0; +} + +int +spdk_nvmf_parse_conf(spdk_nvmf_parse_conf_done_fn cb_fn) +{ + int rc; + + if (cb_fn == NULL) { + SPDK_ERRLOG("Callback function is NULL\n"); + return -1; + } + + /* NVMf section */ + rc = spdk_nvmf_parse_nvmf_tgt(); + if (rc < 0) { + return rc; + } + + /* Transport sections */ + rc = spdk_nvmf_parse_transports(cb_fn); + if (rc < 0) { + return rc; + } + + return 0; +} diff --git a/src/spdk/lib/event/subsystems/nvmf/event_nvmf.h b/src/spdk/lib/event/subsystems/nvmf/event_nvmf.h new file mode 100644 index 00000000..50e5d755 --- /dev/null +++ b/src/spdk/lib/event/subsystems/nvmf/event_nvmf.h @@ -0,0 +1,67 @@ +/*- + * 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. + */ + +#ifndef NVMF_TGT_H +#define NVMF_TGT_H + +#include "spdk/stdinc.h" + +#include "spdk/nvmf.h" +#include "spdk/queue.h" + +#include "spdk_internal/event.h" +#include "spdk_internal/log.h" + +#define ACCEPT_TIMEOUT_US 10000 /* 10ms */ +#define DEFAULT_CONN_SCHED CONNECT_SCHED_ROUND_ROBIN + +enum spdk_nvmf_connect_sched { + CONNECT_SCHED_ROUND_ROBIN = 0, + CONNECT_SCHED_HOST_IP, +}; + +struct spdk_nvmf_tgt_conf { + uint32_t acceptor_poll_rate; + enum spdk_nvmf_connect_sched conn_sched; +}; + +extern struct spdk_nvmf_tgt_opts *g_spdk_nvmf_tgt_opts; +extern struct spdk_nvmf_tgt_conf *g_spdk_nvmf_tgt_conf; + +extern struct spdk_nvmf_tgt *g_spdk_nvmf_tgt; + +typedef void (*spdk_nvmf_parse_conf_done_fn)(int status); + +int spdk_nvmf_parse_conf(spdk_nvmf_parse_conf_done_fn cb_fn); + +#endif diff --git a/src/spdk/lib/event/subsystems/nvmf/nvmf_rpc.c b/src/spdk/lib/event/subsystems/nvmf/nvmf_rpc.c new file mode 100644 index 00000000..e4114afe --- /dev/null +++ b/src/spdk/lib/event/subsystems/nvmf/nvmf_rpc.c @@ -0,0 +1,1562 @@ +/*- + * 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 "event_nvmf.h" + +#include "spdk/bdev.h" +#include "spdk/log.h" +#include "spdk/rpc.h" +#include "spdk/env.h" +#include "spdk/nvme.h" +#include "spdk/nvmf.h" +#include "spdk/string.h" +#include "spdk/util.h" + +static int +json_write_hex_str(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + static const char hex_char[16] = "0123456789ABCDEF"; + const uint8_t *buf = data; + char *str, *out; + int rc; + + str = malloc(size * 2 + 1); + if (str == NULL) { + return -1; + } + + out = str; + while (size--) { + unsigned byte = *buf++; + + out[0] = hex_char[(byte >> 4) & 0xF]; + out[1] = hex_char[byte & 0xF]; + + out += 2; + } + *out = '\0'; + + rc = spdk_json_write_string(w, str); + free(str); + + return rc; +} + +static int +hex_nybble_to_num(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + + if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xA; + } + + if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xA; + } + + return -1; +} + +static int +hex_byte_to_num(const char *str) +{ + int hi, lo; + + hi = hex_nybble_to_num(str[0]); + if (hi < 0) { + return hi; + } + + lo = hex_nybble_to_num(str[1]); + if (lo < 0) { + return lo; + } + + return hi * 16 + lo; +} + +static int +decode_hex_string_be(const char *str, uint8_t *out, size_t size) +{ + size_t i; + + /* Decode a string in "ABCDEF012345" format to its binary representation */ + for (i = 0; i < size; i++) { + int num = hex_byte_to_num(str); + + if (num < 0) { + /* Invalid hex byte or end of string */ + return -1; + } + + out[i] = (uint8_t)num; + str += 2; + } + + if (i != size || *str != '\0') { + /* Length mismatch */ + return -1; + } + + return 0; +} + +static int +decode_ns_nguid(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + /* 16-byte NGUID */ + rc = decode_hex_string_be(str, out, 16); + } + + free(str); + return rc; +} + +static int +decode_ns_eui64(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + /* 8-byte EUI-64 */ + rc = decode_hex_string_be(str, out, 8); + } + + free(str); + return rc; +} + +static int +decode_ns_uuid(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + rc = spdk_uuid_parse(out, str); + } + + free(str); + return rc; +} + +static void +dump_nvmf_subsystem(struct spdk_json_write_ctx *w, struct spdk_nvmf_subsystem *subsystem) +{ + struct spdk_nvmf_host *host; + struct spdk_nvmf_listener *listener; + + spdk_json_write_object_begin(w); + + spdk_json_write_name(w, "nqn"); + spdk_json_write_string(w, spdk_nvmf_subsystem_get_nqn(subsystem)); + spdk_json_write_name(w, "subtype"); + if (spdk_nvmf_subsystem_get_type(subsystem) == SPDK_NVMF_SUBTYPE_NVME) { + spdk_json_write_string(w, "NVMe"); + } else { + spdk_json_write_string(w, "Discovery"); + } + + spdk_json_write_name(w, "listen_addresses"); + spdk_json_write_array_begin(w); + + for (listener = spdk_nvmf_subsystem_get_first_listener(subsystem); listener != NULL; + listener = spdk_nvmf_subsystem_get_next_listener(subsystem, listener)) { + const struct spdk_nvme_transport_id *trid; + const char *trtype; + const char *adrfam; + + trid = spdk_nvmf_listener_get_trid(listener); + + spdk_json_write_object_begin(w); + trtype = spdk_nvme_transport_id_trtype_str(trid->trtype); + if (trtype == NULL) { + trtype = "unknown"; + } + adrfam = spdk_nvme_transport_id_adrfam_str(trid->adrfam); + if (adrfam == NULL) { + adrfam = "unknown"; + } + /* NOTE: "transport" is kept for compatibility; new code should use "trtype" */ + spdk_json_write_name(w, "transport"); + spdk_json_write_string(w, trtype); + spdk_json_write_name(w, "trtype"); + spdk_json_write_string(w, trtype); + spdk_json_write_name(w, "adrfam"); + spdk_json_write_string(w, adrfam); + spdk_json_write_name(w, "traddr"); + spdk_json_write_string(w, trid->traddr); + spdk_json_write_name(w, "trsvcid"); + spdk_json_write_string(w, trid->trsvcid); + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + spdk_json_write_name(w, "allow_any_host"); + spdk_json_write_bool(w, spdk_nvmf_subsystem_get_allow_any_host(subsystem)); + + spdk_json_write_name(w, "hosts"); + spdk_json_write_array_begin(w); + + for (host = spdk_nvmf_subsystem_get_first_host(subsystem); host != NULL; + host = spdk_nvmf_subsystem_get_next_host(subsystem, host)) { + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "nqn"); + spdk_json_write_string(w, spdk_nvmf_host_get_nqn(host)); + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + if (spdk_nvmf_subsystem_get_type(subsystem) == SPDK_NVMF_SUBTYPE_NVME) { + struct spdk_nvmf_ns *ns; + struct spdk_nvmf_ns_opts ns_opts; + uint32_t max_namespaces; + + spdk_json_write_name(w, "serial_number"); + spdk_json_write_string(w, spdk_nvmf_subsystem_get_sn(subsystem)); + + max_namespaces = spdk_nvmf_subsystem_get_max_namespaces(subsystem); + if (max_namespaces != 0) { + spdk_json_write_named_uint32(w, "max_namespaces", max_namespaces); + } + + spdk_json_write_name(w, "namespaces"); + spdk_json_write_array_begin(w); + for (ns = spdk_nvmf_subsystem_get_first_ns(subsystem); ns != NULL; + ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns)) { + spdk_nvmf_ns_get_opts(ns, &ns_opts, sizeof(ns_opts)); + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "nsid"); + spdk_json_write_int32(w, spdk_nvmf_ns_get_id(ns)); + spdk_json_write_name(w, "bdev_name"); + spdk_json_write_string(w, spdk_bdev_get_name(spdk_nvmf_ns_get_bdev(ns))); + /* NOTE: "name" is kept for compatibility only - new code should use bdev_name. */ + spdk_json_write_name(w, "name"); + spdk_json_write_string(w, spdk_bdev_get_name(spdk_nvmf_ns_get_bdev(ns))); + + if (!spdk_mem_all_zero(ns_opts.nguid, sizeof(ns_opts.nguid))) { + spdk_json_write_name(w, "nguid"); + json_write_hex_str(w, ns_opts.nguid, sizeof(ns_opts.nguid)); + } + + if (!spdk_mem_all_zero(ns_opts.eui64, sizeof(ns_opts.eui64))) { + spdk_json_write_name(w, "eui64"); + json_write_hex_str(w, ns_opts.eui64, sizeof(ns_opts.eui64)); + } + + if (!spdk_mem_all_zero(&ns_opts.uuid, sizeof(ns_opts.uuid))) { + char uuid_str[SPDK_UUID_STRING_LEN]; + + spdk_uuid_fmt_lower(uuid_str, sizeof(uuid_str), &ns_opts.uuid); + spdk_json_write_name(w, "uuid"); + spdk_json_write_string(w, uuid_str); + } + + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + } + spdk_json_write_object_end(w); +} + +static void +spdk_rpc_get_nvmf_subsystems(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct spdk_json_write_ctx *w; + struct spdk_nvmf_subsystem *subsystem; + + if (params != NULL) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "get_nvmf_subsystems requires no parameters"); + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_array_begin(w); + subsystem = spdk_nvmf_subsystem_get_first(g_spdk_nvmf_tgt); + while (subsystem) { + dump_nvmf_subsystem(w, subsystem); + subsystem = spdk_nvmf_subsystem_get_next(subsystem); + } + spdk_json_write_array_end(w); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("get_nvmf_subsystems", spdk_rpc_get_nvmf_subsystems, SPDK_RPC_RUNTIME) + +struct rpc_subsystem_create { + char *nqn; + char *serial_number; + uint32_t max_namespaces; + bool allow_any_host; +}; + +static const struct spdk_json_object_decoder rpc_subsystem_create_decoders[] = { + {"nqn", offsetof(struct rpc_subsystem_create, nqn), spdk_json_decode_string}, + {"serial_number", offsetof(struct rpc_subsystem_create, serial_number), spdk_json_decode_string, true}, + {"max_namespaces", offsetof(struct rpc_subsystem_create, max_namespaces), spdk_json_decode_uint32, true}, + {"allow_any_host", offsetof(struct rpc_subsystem_create, allow_any_host), spdk_json_decode_bool, true}, +}; + +static void +spdk_rpc_nvmf_subsystem_started(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct spdk_jsonrpc_request *request = cb_arg; + struct spdk_json_write_ctx *w; + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static void +spdk_rpc_nvmf_subsystem_create(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_subsystem_create *req; + struct spdk_nvmf_subsystem *subsystem; + + req = calloc(1, sizeof(*req)); + if (!req) { + goto invalid; + } + + if (spdk_json_decode_object(params, rpc_subsystem_create_decoders, + SPDK_COUNTOF(rpc_subsystem_create_decoders), + req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + goto invalid; + } + + subsystem = spdk_nvmf_subsystem_create(g_spdk_nvmf_tgt, req->nqn, SPDK_NVMF_SUBTYPE_NVME, + req->max_namespaces); + if (!subsystem) { + goto invalid; + } + + if (req->serial_number) { + if (spdk_nvmf_subsystem_set_sn(subsystem, req->serial_number)) { + SPDK_ERRLOG("Subsystem %s: invalid serial number '%s'\n", req->nqn, req->serial_number); + goto invalid; + } + } + + spdk_nvmf_subsystem_set_allow_any_host(subsystem, req->allow_any_host); + + free(req->nqn); + free(req->serial_number); + free(req); + + spdk_nvmf_subsystem_start(subsystem, + spdk_rpc_nvmf_subsystem_started, + request); + + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + if (req) { + free(req->nqn); + free(req->serial_number); + } + free(req); +} +SPDK_RPC_REGISTER("nvmf_subsystem_create", spdk_rpc_nvmf_subsystem_create, SPDK_RPC_RUNTIME) + +struct rpc_delete_subsystem { + char *nqn; +}; + +static void +free_rpc_delete_subsystem(struct rpc_delete_subsystem *r) +{ + free(r->nqn); +} + +static void +spdk_rpc_nvmf_subsystem_stopped(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct spdk_jsonrpc_request *request = cb_arg; + struct spdk_json_write_ctx *w; + + spdk_nvmf_subsystem_destroy(subsystem); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static const struct spdk_json_object_decoder rpc_delete_subsystem_decoders[] = { + {"nqn", offsetof(struct rpc_delete_subsystem, nqn), spdk_json_decode_string}, +}; + +static void +spdk_rpc_delete_nvmf_subsystem(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_delete_subsystem req = {}; + struct spdk_nvmf_subsystem *subsystem; + + if (spdk_json_decode_object(params, rpc_delete_subsystem_decoders, + SPDK_COUNTOF(rpc_delete_subsystem_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + goto invalid; + } + + if (req.nqn == NULL) { + SPDK_ERRLOG("missing name param\n"); + goto invalid; + } + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, req.nqn); + if (!subsystem) { + goto invalid; + } + + free_rpc_delete_subsystem(&req); + + spdk_nvmf_subsystem_stop(subsystem, + spdk_rpc_nvmf_subsystem_stopped, + request); + + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_delete_subsystem(&req); +} +SPDK_RPC_REGISTER("delete_nvmf_subsystem", spdk_rpc_delete_nvmf_subsystem, SPDK_RPC_RUNTIME) + +struct rpc_listen_address { + char *transport; + char *adrfam; + char *traddr; + char *trsvcid; +}; + +#define RPC_MAX_LISTEN_ADDRESSES 255 +#define RPC_MAX_NAMESPACES 255 + +struct rpc_listen_addresses { + size_t num_listen_address; + struct rpc_listen_address addresses[RPC_MAX_LISTEN_ADDRESSES]; +}; + +static const struct spdk_json_object_decoder rpc_listen_address_decoders[] = { + /* NOTE: "transport" is kept for compatibility; new code should use "trtype" */ + {"transport", offsetof(struct rpc_listen_address, transport), spdk_json_decode_string, true}, + {"trtype", offsetof(struct rpc_listen_address, transport), spdk_json_decode_string, true}, + {"adrfam", offsetof(struct rpc_listen_address, adrfam), spdk_json_decode_string, true}, + {"traddr", offsetof(struct rpc_listen_address, traddr), spdk_json_decode_string}, + {"trsvcid", offsetof(struct rpc_listen_address, trsvcid), spdk_json_decode_string}, +}; + +static int +decode_rpc_listen_address(const struct spdk_json_val *val, void *out) +{ + struct rpc_listen_address *req = (struct rpc_listen_address *)out; + if (spdk_json_decode_object(val, rpc_listen_address_decoders, + SPDK_COUNTOF(rpc_listen_address_decoders), + req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + return -1; + } + return 0; +} + +static void +free_rpc_listen_address(struct rpc_listen_address *r) +{ + free(r->transport); + free(r->adrfam); + free(r->traddr); + free(r->trsvcid); +} + +enum nvmf_rpc_listen_op { + NVMF_RPC_LISTEN_ADD, + NVMF_RPC_LISTEN_REMOVE, +}; + +struct nvmf_rpc_listener_ctx { + char *nqn; + struct spdk_nvmf_subsystem *subsystem; + struct rpc_listen_address address; + + struct spdk_jsonrpc_request *request; + struct spdk_nvme_transport_id trid; + enum nvmf_rpc_listen_op op; + bool response_sent; +}; + +static const struct spdk_json_object_decoder nvmf_rpc_listener_decoder[] = { + {"nqn", offsetof(struct nvmf_rpc_listener_ctx, nqn), spdk_json_decode_string}, + {"listen_address", offsetof(struct nvmf_rpc_listener_ctx, address), decode_rpc_listen_address}, +}; + +static void +nvmf_rpc_listener_ctx_free(struct nvmf_rpc_listener_ctx *ctx) +{ + free(ctx->nqn); + free_rpc_listen_address(&ctx->address); + free(ctx); +} + +static void +nvmf_rpc_listen_resumed(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_listener_ctx *ctx = cb_arg; + struct spdk_jsonrpc_request *request; + struct spdk_json_write_ctx *w; + + request = ctx->request; + if (ctx->response_sent) { + /* If an error occurred, the response has already been sent. */ + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + nvmf_rpc_listener_ctx_free(ctx); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static void +nvmf_rpc_tgt_listen(void *cb_arg, int status) +{ + struct nvmf_rpc_listener_ctx *ctx = cb_arg; + + if (status) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + } else { + if (spdk_nvmf_subsystem_add_listener(ctx->subsystem, &ctx->trid)) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + } + } + + if (spdk_nvmf_subsystem_resume(ctx->subsystem, nvmf_rpc_listen_resumed, ctx)) { + if (!ctx->response_sent) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + } + nvmf_rpc_listener_ctx_free(ctx); + /* Can't really do anything to recover here - subsystem will remain paused. */ + } +} + +static void +nvmf_rpc_listen_paused(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_listener_ctx *ctx = cb_arg; + + if (ctx->op == NVMF_RPC_LISTEN_ADD) { + spdk_nvmf_tgt_listen(g_spdk_nvmf_tgt, &ctx->trid, nvmf_rpc_tgt_listen, ctx); + return; + } else if (ctx->op == NVMF_RPC_LISTEN_REMOVE) { + if (spdk_nvmf_subsystem_remove_listener(subsystem, &ctx->trid)) { + SPDK_ERRLOG("Unable to remove listener.\n"); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + } + } else { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + } + + if (spdk_nvmf_subsystem_resume(subsystem, nvmf_rpc_listen_resumed, ctx)) { + if (!ctx->response_sent) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + } + nvmf_rpc_listener_ctx_free(ctx); + /* Can't really do anything to recover here - subsystem will remain paused. */ + } +} + +static int +rpc_listen_address_to_trid(const struct rpc_listen_address *address, + struct spdk_nvme_transport_id *trid) +{ + size_t len; + + memset(trid, 0, sizeof(*trid)); + + if (spdk_nvme_transport_id_parse_trtype(&trid->trtype, address->transport)) { + SPDK_ERRLOG("Invalid transport type: %s\n", address->transport); + return -EINVAL; + } + + if (address->adrfam) { + if (spdk_nvme_transport_id_parse_adrfam(&trid->adrfam, address->adrfam)) { + SPDK_ERRLOG("Invalid adrfam: %s\n", address->adrfam); + return -EINVAL; + } + } else { + trid->adrfam = SPDK_NVMF_ADRFAM_IPV4; + } + + len = strlen(address->traddr); + if (len > sizeof(trid->traddr) - 1) { + SPDK_ERRLOG("Transport address longer than %zu characters: %s\n", + sizeof(trid->traddr) - 1, address->traddr); + return -EINVAL; + } + memcpy(trid->traddr, address->traddr, len + 1); + + len = strlen(address->trsvcid); + if (len > sizeof(trid->trsvcid) - 1) { + SPDK_ERRLOG("Transport service id longer than %zu characters: %s\n", + sizeof(trid->trsvcid) - 1, address->trsvcid); + return -EINVAL; + } + memcpy(trid->trsvcid, address->trsvcid, len + 1); + + return 0; +} + +static void +nvmf_rpc_subsystem_add_listener(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_listener_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + ctx->request = request; + + if (spdk_json_decode_object(params, nvmf_rpc_listener_decoder, + SPDK_COUNTOF(nvmf_rpc_listener_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + ctx->subsystem = subsystem; + + if (rpc_listen_address_to_trid(&ctx->address, &ctx->trid)) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + ctx->op = NVMF_RPC_LISTEN_ADD; + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_listen_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } +} +SPDK_RPC_REGISTER("nvmf_subsystem_add_listener", nvmf_rpc_subsystem_add_listener, SPDK_RPC_RUNTIME); + +static void +nvmf_rpc_subsystem_remove_listener(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_listener_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + ctx->request = request; + + if (spdk_json_decode_object(params, nvmf_rpc_listener_decoder, + SPDK_COUNTOF(nvmf_rpc_listener_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + ctx->subsystem = subsystem; + + if (rpc_listen_address_to_trid(&ctx->address, &ctx->trid)) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + ctx->op = NVMF_RPC_LISTEN_REMOVE; + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_listen_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + +} +SPDK_RPC_REGISTER("nvmf_subsystem_remove_listener", nvmf_rpc_subsystem_remove_listener, + SPDK_RPC_RUNTIME); + +struct spdk_nvmf_ns_params { + char *bdev_name; + uint32_t nsid; + char nguid[16]; + char eui64[8]; + struct spdk_uuid uuid; +}; + +struct rpc_namespaces { + size_t num_ns; + struct spdk_nvmf_ns_params ns_params[RPC_MAX_NAMESPACES]; +}; + + +static const struct spdk_json_object_decoder rpc_ns_params_decoders[] = { + {"nsid", offsetof(struct spdk_nvmf_ns_params, nsid), spdk_json_decode_uint32, true}, + {"bdev_name", offsetof(struct spdk_nvmf_ns_params, bdev_name), spdk_json_decode_string}, + {"nguid", offsetof(struct spdk_nvmf_ns_params, nguid), decode_ns_nguid, true}, + {"eui64", offsetof(struct spdk_nvmf_ns_params, eui64), decode_ns_eui64, true}, + {"uuid", offsetof(struct spdk_nvmf_ns_params, uuid), decode_ns_uuid, true}, +}; + +static int +decode_rpc_ns_params(const struct spdk_json_val *val, void *out) +{ + struct spdk_nvmf_ns_params *ns_params = out; + + return spdk_json_decode_object(val, rpc_ns_params_decoders, + SPDK_COUNTOF(rpc_ns_params_decoders), + ns_params); +} + +struct nvmf_rpc_ns_ctx { + char *nqn; + struct spdk_nvmf_ns_params ns_params; + + struct spdk_jsonrpc_request *request; + bool response_sent; +}; + +static const struct spdk_json_object_decoder nvmf_rpc_subsystem_ns_decoder[] = { + {"nqn", offsetof(struct nvmf_rpc_ns_ctx, nqn), spdk_json_decode_string}, + {"namespace", offsetof(struct nvmf_rpc_ns_ctx, ns_params), decode_rpc_ns_params}, +}; + +static void +nvmf_rpc_ns_ctx_free(struct nvmf_rpc_ns_ctx *ctx) +{ + free(ctx->nqn); + free(ctx->ns_params.bdev_name); + free(ctx); +} + +static void +nvmf_rpc_ns_resumed(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_ns_ctx *ctx = cb_arg; + struct spdk_jsonrpc_request *request = ctx->request; + uint32_t nsid = ctx->ns_params.nsid; + bool response_sent = ctx->response_sent; + struct spdk_json_write_ctx *w; + + nvmf_rpc_ns_ctx_free(ctx); + + if (response_sent) { + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_uint32(w, nsid); + spdk_jsonrpc_end_result(request, w); +} + +static void +nvmf_rpc_ns_paused(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_ns_ctx *ctx = cb_arg; + struct spdk_nvmf_ns_opts ns_opts; + struct spdk_bdev *bdev; + + bdev = spdk_bdev_get_by_name(ctx->ns_params.bdev_name); + if (!bdev) { + SPDK_ERRLOG("No bdev with name %s\n", ctx->ns_params.bdev_name); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + goto resume; + } + + spdk_nvmf_ns_opts_get_defaults(&ns_opts, sizeof(ns_opts)); + ns_opts.nsid = ctx->ns_params.nsid; + + SPDK_STATIC_ASSERT(sizeof(ns_opts.nguid) == sizeof(ctx->ns_params.nguid), "size mismatch"); + memcpy(ns_opts.nguid, ctx->ns_params.nguid, sizeof(ns_opts.nguid)); + + SPDK_STATIC_ASSERT(sizeof(ns_opts.eui64) == sizeof(ctx->ns_params.eui64), "size mismatch"); + memcpy(ns_opts.eui64, ctx->ns_params.eui64, sizeof(ns_opts.eui64)); + + if (!spdk_mem_all_zero(&ctx->ns_params.uuid, sizeof(ctx->ns_params.uuid))) { + ns_opts.uuid = ctx->ns_params.uuid; + } + + ctx->ns_params.nsid = spdk_nvmf_subsystem_add_ns(subsystem, bdev, &ns_opts, sizeof(ns_opts)); + if (ctx->ns_params.nsid == 0) { + SPDK_ERRLOG("Unable to add namespace\n"); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + goto resume; + } + +resume: + if (spdk_nvmf_subsystem_resume(subsystem, nvmf_rpc_ns_resumed, ctx)) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_ns_ctx_free(ctx); + return; + } +} + +static void +nvmf_rpc_subsystem_add_ns(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_ns_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + if (spdk_json_decode_object(params, nvmf_rpc_subsystem_ns_decoder, + SPDK_COUNTOF(nvmf_rpc_subsystem_ns_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_ns_ctx_free(ctx); + return; + } + + ctx->request = request; + ctx->response_sent = false; + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_ns_ctx_free(ctx); + return; + } + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_ns_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_ns_ctx_free(ctx); + return; + } +} +SPDK_RPC_REGISTER("nvmf_subsystem_add_ns", nvmf_rpc_subsystem_add_ns, SPDK_RPC_RUNTIME) + +struct nvmf_rpc_remove_ns_ctx { + char *nqn; + uint32_t nsid; + + struct spdk_jsonrpc_request *request; + bool response_sent; +}; + +static const struct spdk_json_object_decoder nvmf_rpc_subsystem_remove_ns_decoder[] = { + {"nqn", offsetof(struct nvmf_rpc_remove_ns_ctx, nqn), spdk_json_decode_string}, + {"nsid", offsetof(struct nvmf_rpc_remove_ns_ctx, nsid), spdk_json_decode_uint32}, +}; + +static void +nvmf_rpc_remove_ns_ctx_free(struct nvmf_rpc_remove_ns_ctx *ctx) +{ + free(ctx->nqn); + free(ctx); +} + +static void +nvmf_rpc_remove_ns_resumed(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_remove_ns_ctx *ctx = cb_arg; + struct spdk_jsonrpc_request *request = ctx->request; + bool response_sent = ctx->response_sent; + struct spdk_json_write_ctx *w; + + nvmf_rpc_remove_ns_ctx_free(ctx); + + if (response_sent) { + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static void +nvmf_rpc_remove_ns_remove_done(struct spdk_nvmf_subsystem *subsystem, void *cb_arg, int status) +{ + struct nvmf_rpc_remove_ns_ctx *ctx; + + ctx = cb_arg; + + if (status != 0) { + SPDK_ERRLOG("Unable to remove namespace ID %u\n", ctx->nsid); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + } + + if (spdk_nvmf_subsystem_resume(subsystem, nvmf_rpc_remove_ns_resumed, ctx)) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_remove_ns_ctx_free(ctx); + return; + } +} + +static void +nvmf_rpc_remove_ns_paused(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_remove_ns_ctx *ctx = cb_arg; + int ret; + + ret = spdk_nvmf_subsystem_remove_ns(subsystem, ctx->nsid, nvmf_rpc_remove_ns_remove_done, ctx); + if (ret < 0) { + SPDK_ERRLOG("Unable to remove namespace ID %u\n", ctx->nsid); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + ctx->response_sent = true; + spdk_nvmf_subsystem_resume(subsystem, nvmf_rpc_remove_ns_resumed, ctx); + } +} + +static void +nvmf_rpc_subsystem_remove_ns(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_remove_ns_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + if (spdk_json_decode_object(params, nvmf_rpc_subsystem_remove_ns_decoder, + SPDK_COUNTOF(nvmf_rpc_subsystem_remove_ns_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_remove_ns_ctx_free(ctx); + return; + } + + ctx->request = request; + ctx->response_sent = false; + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_remove_ns_ctx_free(ctx); + return; + } + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_remove_ns_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_remove_ns_ctx_free(ctx); + return; + } +} +SPDK_RPC_REGISTER("nvmf_subsystem_remove_ns", nvmf_rpc_subsystem_remove_ns, SPDK_RPC_RUNTIME) + +enum nvmf_rpc_host_op { + NVMF_RPC_HOST_ADD, + NVMF_RPC_HOST_REMOVE, + NVMF_RPC_HOST_ALLOW_ANY, +}; + +struct nvmf_rpc_host_ctx { + struct spdk_jsonrpc_request *request; + + char *nqn; + char *host; + + enum nvmf_rpc_host_op op; + + bool allow_any_host; + + bool response_sent; +}; + +static const struct spdk_json_object_decoder nvmf_rpc_subsystem_host_decoder[] = { + {"nqn", offsetof(struct nvmf_rpc_host_ctx, nqn), spdk_json_decode_string}, + {"host", offsetof(struct nvmf_rpc_host_ctx, host), spdk_json_decode_string}, +}; + +static void +nvmf_rpc_host_ctx_free(struct nvmf_rpc_host_ctx *ctx) +{ + free(ctx->nqn); + free(ctx->host); + free(ctx); +} + +static void +nvmf_rpc_host_resumed(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_host_ctx *ctx = cb_arg; + struct spdk_jsonrpc_request *request; + struct spdk_json_write_ctx *w; + bool response_sent = ctx->response_sent; + + request = ctx->request; + nvmf_rpc_host_ctx_free(ctx); + + if (response_sent) { + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static void +nvmf_rpc_host_paused(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct nvmf_rpc_host_ctx *ctx = cb_arg; + int rc = -1; + + switch (ctx->op) { + case NVMF_RPC_HOST_ADD: + rc = spdk_nvmf_subsystem_add_host(subsystem, ctx->host); + break; + case NVMF_RPC_HOST_REMOVE: + rc = spdk_nvmf_subsystem_remove_host(subsystem, ctx->host); + break; + case NVMF_RPC_HOST_ALLOW_ANY: + rc = spdk_nvmf_subsystem_set_allow_any_host(subsystem, ctx->allow_any_host); + break; + } + + if (rc != 0) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + ctx->response_sent = true; + } + + if (spdk_nvmf_subsystem_resume(subsystem, nvmf_rpc_host_resumed, ctx)) { + if (!ctx->response_sent) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + } + nvmf_rpc_host_ctx_free(ctx); + return; + } +} + +static void +nvmf_rpc_subsystem_add_host(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_host_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + if (spdk_json_decode_object(params, nvmf_rpc_subsystem_host_decoder, + SPDK_COUNTOF(nvmf_rpc_subsystem_host_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_host_ctx_free(ctx); + return; + } + + ctx->request = request; + ctx->op = NVMF_RPC_HOST_ADD; + ctx->response_sent = false; + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_host_ctx_free(ctx); + return; + } + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_host_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_host_ctx_free(ctx); + return; + } +} +SPDK_RPC_REGISTER("nvmf_subsystem_add_host", nvmf_rpc_subsystem_add_host, SPDK_RPC_RUNTIME) + +static void +nvmf_rpc_subsystem_remove_host(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_host_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + if (spdk_json_decode_object(params, nvmf_rpc_subsystem_host_decoder, + SPDK_COUNTOF(nvmf_rpc_subsystem_host_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_host_ctx_free(ctx); + return; + } + + ctx->request = request; + ctx->op = NVMF_RPC_HOST_REMOVE; + ctx->response_sent = false; + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_host_ctx_free(ctx); + return; + } + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_host_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_host_ctx_free(ctx); + return; + } +} +SPDK_RPC_REGISTER("nvmf_subsystem_remove_host", nvmf_rpc_subsystem_remove_host, SPDK_RPC_RUNTIME) + + +static const struct spdk_json_object_decoder nvmf_rpc_subsystem_any_host_decoder[] = { + {"nqn", offsetof(struct nvmf_rpc_host_ctx, nqn), spdk_json_decode_string}, + {"allow_any_host", offsetof(struct nvmf_rpc_host_ctx, allow_any_host), spdk_json_decode_bool}, +}; + +static void +nvmf_rpc_subsystem_allow_any_host(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_host_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + if (spdk_json_decode_object(params, nvmf_rpc_subsystem_any_host_decoder, + SPDK_COUNTOF(nvmf_rpc_subsystem_any_host_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_host_ctx_free(ctx); + return; + } + + ctx->request = request; + ctx->op = NVMF_RPC_HOST_ALLOW_ANY; + ctx->response_sent = false; + + subsystem = spdk_nvmf_tgt_find_subsystem(g_spdk_nvmf_tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_host_ctx_free(ctx); + return; + } + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_host_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + nvmf_rpc_host_ctx_free(ctx); + return; + } +} +SPDK_RPC_REGISTER("nvmf_subsystem_allow_any_host", nvmf_rpc_subsystem_allow_any_host, + SPDK_RPC_RUNTIME) + +static const struct spdk_json_object_decoder nvmf_rpc_subsystem_tgt_opts_decoder[] = { + {"max_queue_depth", offsetof(struct spdk_nvmf_tgt_opts, max_queue_depth), spdk_json_decode_uint16, true}, + {"max_qpairs_per_ctrlr", offsetof(struct spdk_nvmf_tgt_opts, max_qpairs_per_ctrlr), spdk_json_decode_uint16, true}, + {"in_capsule_data_size", offsetof(struct spdk_nvmf_tgt_opts, in_capsule_data_size), spdk_json_decode_uint32, true}, + {"max_io_size", offsetof(struct spdk_nvmf_tgt_opts, max_io_size), spdk_json_decode_uint32, true}, + {"max_subsystems", offsetof(struct spdk_nvmf_tgt_opts, max_subsystems), spdk_json_decode_uint32, true}, + {"io_unit_size", offsetof(struct spdk_nvmf_tgt_opts, io_unit_size), spdk_json_decode_uint32, true}, +}; + +static void +nvmf_rpc_subsystem_set_tgt_opts(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct spdk_nvmf_tgt_opts *opts; + struct spdk_json_write_ctx *w; + + if (g_spdk_nvmf_tgt_opts != NULL) { + SPDK_ERRLOG("this RPC must not be called more than once.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Must not call more than once"); + return; + } + + opts = calloc(1, sizeof(*opts)); + if (opts == NULL) { + SPDK_ERRLOG("malloc() failed for target options\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Out of memory"); + return; + } + + spdk_nvmf_tgt_opts_init(opts); + + if (params != NULL) { + if (spdk_json_decode_object(params, nvmf_rpc_subsystem_tgt_opts_decoder, + SPDK_COUNTOF(nvmf_rpc_subsystem_tgt_opts_decoder), opts)) { + free(opts); + SPDK_ERRLOG("spdk_json_decode_object() failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + return; + } + } + + g_spdk_nvmf_tgt_opts = opts; + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("set_nvmf_target_options", nvmf_rpc_subsystem_set_tgt_opts, SPDK_RPC_STARTUP) + +static int decode_conn_sched(const struct spdk_json_val *val, void *out) +{ + enum spdk_nvmf_connect_sched *sched = out; + + if (spdk_json_strequal(val, "roundrobin") == true) { + *sched = CONNECT_SCHED_ROUND_ROBIN; + } else if (spdk_json_strequal(val, "hostip") == true) { + *sched = CONNECT_SCHED_HOST_IP; + } else { + SPDK_ERRLOG("Invalid connection scheduling parameter\n"); + return -EINVAL; + } + + return 0; +} + +static const struct spdk_json_object_decoder nvmf_rpc_subsystem_tgt_conf_decoder[] = { + {"acceptor_poll_rate", offsetof(struct spdk_nvmf_tgt_conf, acceptor_poll_rate), spdk_json_decode_uint32, true}, + {"conn_sched", offsetof(struct spdk_nvmf_tgt_conf, conn_sched), decode_conn_sched, true}, +}; + +static void +nvmf_rpc_subsystem_set_tgt_conf(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct spdk_nvmf_tgt_conf *conf; + struct spdk_json_write_ctx *w; + + if (g_spdk_nvmf_tgt_conf != NULL) { + SPDK_ERRLOG("this RPC must not be called more than once.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Must not call more than once"); + return; + } + + conf = calloc(1, sizeof(*conf)); + if (conf == NULL) { + SPDK_ERRLOG("calloc() failed for target config\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Out of memory"); + return; + } + + conf->acceptor_poll_rate = ACCEPT_TIMEOUT_US; + conf->conn_sched = DEFAULT_CONN_SCHED; + + if (params != NULL) { + if (spdk_json_decode_object(params, nvmf_rpc_subsystem_tgt_conf_decoder, + SPDK_COUNTOF(nvmf_rpc_subsystem_tgt_conf_decoder), conf)) { + free(conf); + SPDK_ERRLOG("spdk_json_decode_object() failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + return; + } + } + + g_spdk_nvmf_tgt_conf = conf; + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("set_nvmf_target_config", nvmf_rpc_subsystem_set_tgt_conf, SPDK_RPC_STARTUP) + +struct nvmf_rpc_create_transport_ctx { + char *trtype; + struct spdk_nvmf_transport_opts opts; + struct spdk_jsonrpc_request *request; +}; + +static const struct spdk_json_object_decoder nvmf_rpc_create_transport_decoder[] = { + { "trtype", offsetof(struct nvmf_rpc_create_transport_ctx, trtype), spdk_json_decode_string}, + { + "max_queue_depth", offsetof(struct nvmf_rpc_create_transport_ctx, opts.max_queue_depth), + spdk_json_decode_uint16, true + }, + { + "max_qpairs_per_ctrlr", offsetof(struct nvmf_rpc_create_transport_ctx, opts.max_qpairs_per_ctrlr), + spdk_json_decode_uint16, true + }, + { + "in_capsule_data_size", offsetof(struct nvmf_rpc_create_transport_ctx, opts.in_capsule_data_size), + spdk_json_decode_uint32, true + }, + { + "max_io_size", offsetof(struct nvmf_rpc_create_transport_ctx, opts.max_io_size), + spdk_json_decode_uint32, true + }, + { + "io_unit_size", offsetof(struct nvmf_rpc_create_transport_ctx, opts.io_unit_size), + spdk_json_decode_uint32, true + }, + { + "max_aq_depth", offsetof(struct nvmf_rpc_create_transport_ctx, opts.max_aq_depth), + spdk_json_decode_uint32, true + }, +}; + +static void +nvmf_rpc_create_transport_ctx_free(struct nvmf_rpc_create_transport_ctx *ctx) +{ + free(ctx->trtype); + free(ctx); +} + +static void +nvmf_rpc_tgt_add_transport_done(void *cb_arg, int status) +{ + struct nvmf_rpc_create_transport_ctx *ctx = cb_arg; + struct spdk_jsonrpc_request *request; + struct spdk_json_write_ctx *w; + + request = ctx->request; + nvmf_rpc_create_transport_ctx_free(ctx); + + if (status) { + SPDK_ERRLOG("Failed to add transport to tgt.(%d)\n", status); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Failed to add transport to tgt.(%d)\n", + status); + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static void +nvmf_rpc_create_transport(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_create_transport_ctx *ctx; + enum spdk_nvme_transport_type trtype; + struct spdk_nvmf_transport *transport; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + /* Decode parameters the first time to get the transport type */ + if (spdk_json_decode_object(params, nvmf_rpc_create_transport_decoder, + SPDK_COUNTOF(nvmf_rpc_create_transport_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_create_transport_ctx_free(ctx); + return; + } + + if (spdk_nvme_transport_id_parse_trtype(&trtype, ctx->trtype)) { + SPDK_ERRLOG("Invalid transport type '%s'\n", ctx->trtype); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid transport type '%s'\n", ctx->trtype); + nvmf_rpc_create_transport_ctx_free(ctx); + return; + } + + /* Initialize all the transport options (based on transport type) and decode the + * parameters again to update any options passed in rpc create transport call. + */ + spdk_nvmf_transport_opts_init(trtype, &ctx->opts); + if (spdk_json_decode_object(params, nvmf_rpc_create_transport_decoder, + SPDK_COUNTOF(nvmf_rpc_create_transport_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + nvmf_rpc_create_transport_ctx_free(ctx); + return; + } + + if (spdk_nvmf_tgt_get_transport(g_spdk_nvmf_tgt, trtype)) { + SPDK_ERRLOG("Transport type '%s' already exists\n", ctx->trtype); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Transport type '%s' already exists\n", ctx->trtype); + nvmf_rpc_create_transport_ctx_free(ctx); + return; + } + + transport = spdk_nvmf_transport_create(trtype, &ctx->opts); + + if (!transport) { + SPDK_ERRLOG("Transport type '%s' create failed\n", ctx->trtype); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Transport type '%s' create failed\n", ctx->trtype); + nvmf_rpc_create_transport_ctx_free(ctx); + return; + } + + /* add transport to target */ + ctx->request = request; + spdk_nvmf_tgt_add_transport(g_spdk_nvmf_tgt, transport, nvmf_rpc_tgt_add_transport_done, ctx); +} + +SPDK_RPC_REGISTER("nvmf_create_transport", nvmf_rpc_create_transport, SPDK_RPC_RUNTIME) diff --git a/src/spdk/lib/event/subsystems/nvmf/nvmf_rpc_deprecated.c b/src/spdk/lib/event/subsystems/nvmf/nvmf_rpc_deprecated.c new file mode 100644 index 00000000..30e5d04c --- /dev/null +++ b/src/spdk/lib/event/subsystems/nvmf/nvmf_rpc_deprecated.c @@ -0,0 +1,620 @@ +/*- + * 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 "event_nvmf.h" + +#include "spdk/bdev.h" +#include "spdk/log.h" +#include "spdk/rpc.h" +#include "spdk/env.h" +#include "spdk/nvme.h" +#include "spdk/nvmf.h" +#include "spdk/string.h" +#include "spdk/util.h" + +static int +hex_nybble_to_num(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + + if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xA; + } + + if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xA; + } + + return -1; +} + +static int +hex_byte_to_num(const char *str) +{ + int hi, lo; + + hi = hex_nybble_to_num(str[0]); + if (hi < 0) { + return hi; + } + + lo = hex_nybble_to_num(str[1]); + if (lo < 0) { + return lo; + } + + return hi * 16 + lo; +} + +static int +decode_hex_string_be(const char *str, uint8_t *out, size_t size) +{ + size_t i; + + /* Decode a string in "ABCDEF012345" format to its binary representation */ + for (i = 0; i < size; i++) { + int num = hex_byte_to_num(str); + + if (num < 0) { + /* Invalid hex byte or end of string */ + return -1; + } + + out[i] = (uint8_t)num; + str += 2; + } + + if (i != size || *str != '\0') { + /* Length mismatch */ + return -1; + } + + return 0; +} + +static int +decode_ns_nguid(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + /* 16-byte NGUID */ + rc = decode_hex_string_be(str, out, 16); + } + + free(str); + return rc; +} + +static int +decode_ns_eui64(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + /* 8-byte EUI-64 */ + rc = decode_hex_string_be(str, out, 8); + } + + free(str); + return rc; +} + +static int +decode_ns_uuid(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + rc = spdk_uuid_parse(out, str); + } + + free(str); + return rc; +} + +struct rpc_listen_address { + char *transport; + char *adrfam; + char *traddr; + char *trsvcid; +}; + +#define RPC_MAX_LISTEN_ADDRESSES 255 +#define RPC_MAX_HOSTS 255 +#define RPC_MAX_NAMESPACES 255 + +struct rpc_listen_addresses { + size_t num_listen_address; + struct rpc_listen_address addresses[RPC_MAX_LISTEN_ADDRESSES]; +}; + +static const struct spdk_json_object_decoder rpc_listen_address_decoders[] = { + /* NOTE: "transport" is kept for compatibility; new code should use "trtype" */ + {"transport", offsetof(struct rpc_listen_address, transport), spdk_json_decode_string, true}, + {"trtype", offsetof(struct rpc_listen_address, transport), spdk_json_decode_string, true}, + {"adrfam", offsetof(struct rpc_listen_address, adrfam), spdk_json_decode_string, true}, + {"traddr", offsetof(struct rpc_listen_address, traddr), spdk_json_decode_string}, + {"trsvcid", offsetof(struct rpc_listen_address, trsvcid), spdk_json_decode_string}, +}; + +static int +decode_rpc_listen_address(const struct spdk_json_val *val, void *out) +{ + struct rpc_listen_address *req = (struct rpc_listen_address *)out; + if (spdk_json_decode_object(val, rpc_listen_address_decoders, + SPDK_COUNTOF(rpc_listen_address_decoders), + req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + return -1; + } + return 0; +} + +static void +free_rpc_listen_address(struct rpc_listen_address *r) +{ + free(r->transport); + free(r->adrfam); + free(r->traddr); + free(r->trsvcid); +} + +static int +rpc_listen_address_to_trid(const struct rpc_listen_address *address, + struct spdk_nvme_transport_id *trid) +{ + size_t len; + + memset(trid, 0, sizeof(*trid)); + + if (spdk_nvme_transport_id_parse_trtype(&trid->trtype, address->transport)) { + SPDK_ERRLOG("Invalid transport type: %s\n", address->transport); + return -EINVAL; + } + + if (address->adrfam) { + if (spdk_nvme_transport_id_parse_adrfam(&trid->adrfam, address->adrfam)) { + SPDK_ERRLOG("Invalid adrfam: %s\n", address->adrfam); + return -EINVAL; + } + } else { + trid->adrfam = SPDK_NVMF_ADRFAM_IPV4; + } + + len = strlen(address->traddr); + if (len > sizeof(trid->traddr) - 1) { + SPDK_ERRLOG("Transport address longer than %zu characters: %s\n", + sizeof(trid->traddr) - 1, address->traddr); + return -EINVAL; + } + memcpy(trid->traddr, address->traddr, len + 1); + + len = strlen(address->trsvcid); + if (len > sizeof(trid->trsvcid) - 1) { + SPDK_ERRLOG("Transport service id longer than %zu characters: %s\n", + sizeof(trid->trsvcid) - 1, address->trsvcid); + return -EINVAL; + } + memcpy(trid->trsvcid, address->trsvcid, len + 1); + + return 0; +} + +static int +decode_rpc_listen_addresses(const struct spdk_json_val *val, void *out) +{ + struct rpc_listen_addresses *listen_addresses = out; + return spdk_json_decode_array(val, decode_rpc_listen_address, &listen_addresses->addresses, + RPC_MAX_LISTEN_ADDRESSES, + &listen_addresses->num_listen_address, sizeof(struct rpc_listen_address)); +} + +struct rpc_hosts { + size_t num_hosts; + char *hosts[RPC_MAX_HOSTS]; +}; + +static int +decode_rpc_hosts(const struct spdk_json_val *val, void *out) +{ + struct rpc_hosts *rpc_hosts = out; + + return spdk_json_decode_array(val, spdk_json_decode_string, rpc_hosts->hosts, RPC_MAX_HOSTS, + &rpc_hosts->num_hosts, sizeof(char *)); +} + + +struct spdk_nvmf_ns_params { + char *bdev_name; + uint32_t nsid; + char nguid[16]; + char eui64[8]; + struct spdk_uuid uuid; +}; + +struct rpc_namespaces { + size_t num_ns; + struct spdk_nvmf_ns_params ns_params[RPC_MAX_NAMESPACES]; +}; + + +static const struct spdk_json_object_decoder rpc_ns_params_decoders[] = { + {"nsid", offsetof(struct spdk_nvmf_ns_params, nsid), spdk_json_decode_uint32, true}, + {"bdev_name", offsetof(struct spdk_nvmf_ns_params, bdev_name), spdk_json_decode_string}, + {"nguid", offsetof(struct spdk_nvmf_ns_params, nguid), decode_ns_nguid, true}, + {"eui64", offsetof(struct spdk_nvmf_ns_params, eui64), decode_ns_eui64, true}, + {"uuid", offsetof(struct spdk_nvmf_ns_params, uuid), decode_ns_uuid, true}, +}; + +static void +free_rpc_ns_params(struct spdk_nvmf_ns_params *ns_params) +{ + free(ns_params->bdev_name); +} + +static void +free_rpc_namespaces(struct rpc_namespaces *r) +{ + size_t i; + + for (i = 0; i < r->num_ns; i++) { + free_rpc_ns_params(&r->ns_params[i]); + } +} + +static int +decode_rpc_ns_params(const struct spdk_json_val *val, void *out) +{ + struct spdk_nvmf_ns_params *ns_params = out; + + return spdk_json_decode_object(val, rpc_ns_params_decoders, + SPDK_COUNTOF(rpc_ns_params_decoders), + ns_params); +} + +static int +decode_rpc_namespaces(const struct spdk_json_val *val, void *out) +{ + struct rpc_namespaces *namespaces = out; + char *names[RPC_MAX_NAMESPACES] = {0}; /* old format - array of strings (bdev names) */ + size_t i; + int rc; + + /* First try to decode namespaces as an array of objects (new format) */ + if (spdk_json_decode_array(val, decode_rpc_ns_params, namespaces->ns_params, + SPDK_COUNTOF(namespaces->ns_params), + &namespaces->num_ns, sizeof(*namespaces->ns_params)) == 0) { + return 0; + } + + /* If that fails, try to decode namespaces as an array of strings (old format) */ + free_rpc_namespaces(namespaces); + memset(namespaces, 0, sizeof(*namespaces)); + rc = spdk_json_decode_array(val, spdk_json_decode_string, names, + SPDK_COUNTOF(names), + &namespaces->num_ns, sizeof(char *)); + if (rc == 0) { + /* Decoded old format - copy to ns_params (new format) */ + for (i = 0; i < namespaces->num_ns; i++) { + namespaces->ns_params[i].bdev_name = names[i]; + } + return 0; + } + + /* Failed to decode - don't leave dangling string pointers around */ + for (i = 0; i < namespaces->num_ns; i++) { + free(names[i]); + } + + return rc; +} + +static void +free_rpc_listen_addresses(struct rpc_listen_addresses *r) +{ + size_t i; + + for (i = 0; i < r->num_listen_address; i++) { + free_rpc_listen_address(&r->addresses[i]); + } +} + +static void +free_rpc_hosts(struct rpc_hosts *r) +{ + size_t i; + + for (i = 0; i < r->num_hosts; i++) { + free(r->hosts[i]); + } +} + +struct rpc_subsystem { + int32_t core; + char *mode; + char *nqn; + struct rpc_listen_addresses listen_addresses; + struct rpc_hosts hosts; + bool allow_any_host; + char *pci_address; + char *serial_number; + struct rpc_namespaces namespaces; + uint32_t num_ns; +}; + +static void +free_rpc_subsystem(struct rpc_subsystem *req) +{ + if (req) { + free(req->mode); + free(req->nqn); + free(req->serial_number); + free_rpc_namespaces(&req->namespaces); + free_rpc_listen_addresses(&req->listen_addresses); + free_rpc_hosts(&req->hosts); + } + free(req); +} + +static void +spdk_rpc_nvmf_subsystem_started(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + struct spdk_jsonrpc_request *request = cb_arg; + struct spdk_json_write_ctx *w; + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static const struct spdk_json_object_decoder rpc_subsystem_decoders[] = { + {"core", offsetof(struct rpc_subsystem, core), spdk_json_decode_int32, true}, + {"mode", offsetof(struct rpc_subsystem, mode), spdk_json_decode_string, true}, + {"nqn", offsetof(struct rpc_subsystem, nqn), spdk_json_decode_string}, + {"listen_addresses", offsetof(struct rpc_subsystem, listen_addresses), decode_rpc_listen_addresses, true}, + {"hosts", offsetof(struct rpc_subsystem, hosts), decode_rpc_hosts, true}, + {"allow_any_host", offsetof(struct rpc_subsystem, allow_any_host), spdk_json_decode_bool, true}, + {"serial_number", offsetof(struct rpc_subsystem, serial_number), spdk_json_decode_string, true}, + {"namespaces", offsetof(struct rpc_subsystem, namespaces), decode_rpc_namespaces, true}, + {"max_namespaces", offsetof(struct rpc_subsystem, num_ns), spdk_json_decode_uint32, true}, +}; + +struct subsystem_listen_ctx { + struct rpc_subsystem *req; + struct spdk_nvmf_subsystem *subsystem; + struct spdk_jsonrpc_request *request; + + uint32_t idx; +}; + +static void +spdk_rpc_construct_subsystem_listen_done(void *cb_arg, int status) +{ + struct subsystem_listen_ctx *ctx = cb_arg; + struct rpc_listen_address *addr; + struct spdk_nvme_transport_id trid = {0}; + + if (status) { + goto invalid; + } + + addr = &ctx->req->listen_addresses.addresses[ctx->idx]; + if (rpc_listen_address_to_trid(addr, &trid)) { + goto invalid; + } + + spdk_nvmf_subsystem_add_listener(ctx->subsystem, &trid); + + ctx->idx++; + + if (ctx->idx < ctx->req->listen_addresses.num_listen_address) { + addr = &ctx->req->listen_addresses.addresses[ctx->idx]; + + if (rpc_listen_address_to_trid(addr, &trid)) { + goto invalid; + } + + spdk_nvmf_tgt_listen(g_spdk_nvmf_tgt, &trid, spdk_rpc_construct_subsystem_listen_done, ctx); + return; + } + + spdk_nvmf_subsystem_start(ctx->subsystem, + spdk_rpc_nvmf_subsystem_started, + ctx->request); + + free_rpc_subsystem(ctx->req); + free(ctx); + + return; + +invalid: + spdk_nvmf_subsystem_destroy(ctx->subsystem); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + free_rpc_subsystem(ctx->req); + free(ctx); +} + +static void +spdk_rpc_construct_nvmf_subsystem(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_subsystem *req; + struct spdk_nvmf_subsystem *subsystem; + size_t i; + + SPDK_WARNLOG("The construct_nvmf_subsystem RPC is deprecated. Use nvmf_subsystem_create instead.\n"); + + req = calloc(1, sizeof(*req)); + if (!req) { + goto invalid; + } + + req->core = -1; /* Explicitly set the core as the uninitialized value */ + + if (spdk_json_decode_object(params, rpc_subsystem_decoders, + SPDK_COUNTOF(rpc_subsystem_decoders), + req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + goto invalid; + } + + /* Mode is no longer a valid parameter, but print out a nice + * message if it exists to inform users. + */ + if (req->mode) { + SPDK_NOTICELOG("Mode present in the construct NVMe-oF subsystem RPC.\n" + "Mode was removed as a valid parameter.\n"); + if (strcasecmp(req->mode, "Virtual") == 0) { + SPDK_NOTICELOG("Your mode value is 'Virtual' which is now the only possible mode.\n" + "Your RPC will work as expected.\n"); + } else { + SPDK_NOTICELOG("Please remove 'mode' from the RPC.\n"); + goto invalid; + } + } + + /* Core is no longer a valid parameter, but print out a nice + * message if it exists to inform users. + */ + if (req->core != -1) { + SPDK_NOTICELOG("Core present in the construct NVMe-oF subsystem RPC.\n" + "Core was removed as an option. Subsystems can now run on all available cores.\n"); + SPDK_NOTICELOG("Ignoring it and continuing.\n"); + } + + subsystem = spdk_nvmf_subsystem_create(g_spdk_nvmf_tgt, req->nqn, SPDK_NVMF_SUBTYPE_NVME, + req->num_ns); + if (!subsystem) { + goto invalid; + } + + if (spdk_nvmf_subsystem_set_sn(subsystem, req->serial_number)) { + SPDK_ERRLOG("Subsystem %s: invalid serial number '%s'\n", req->nqn, req->serial_number); + goto invalid; + } + + for (i = 0; i < req->hosts.num_hosts; i++) { + spdk_nvmf_subsystem_add_host(subsystem, req->hosts.hosts[i]); + } + + spdk_nvmf_subsystem_set_allow_any_host(subsystem, req->allow_any_host); + + for (i = 0; i < req->namespaces.num_ns; i++) { + struct spdk_nvmf_ns_params *ns_params = &req->namespaces.ns_params[i]; + struct spdk_bdev *bdev; + struct spdk_nvmf_ns_opts ns_opts; + + bdev = spdk_bdev_get_by_name(ns_params->bdev_name); + if (bdev == NULL) { + SPDK_ERRLOG("Could not find namespace bdev '%s'\n", ns_params->bdev_name); + spdk_nvmf_subsystem_destroy(subsystem); + goto invalid; + } + + spdk_nvmf_ns_opts_get_defaults(&ns_opts, sizeof(ns_opts)); + ns_opts.nsid = ns_params->nsid; + + SPDK_STATIC_ASSERT(sizeof(ns_opts.nguid) == sizeof(ns_params->nguid), "size mismatch"); + memcpy(ns_opts.nguid, ns_params->nguid, sizeof(ns_opts.nguid)); + + SPDK_STATIC_ASSERT(sizeof(ns_opts.eui64) == sizeof(ns_params->eui64), "size mismatch"); + memcpy(ns_opts.eui64, ns_params->eui64, sizeof(ns_opts.eui64)); + + if (!spdk_mem_all_zero(&ns_params->uuid, sizeof(ns_params->uuid))) { + ns_opts.uuid = ns_params->uuid; + } + + if (spdk_nvmf_subsystem_add_ns(subsystem, bdev, &ns_opts, sizeof(ns_opts)) == 0) { + SPDK_ERRLOG("Unable to add namespace\n"); + spdk_nvmf_subsystem_destroy(subsystem); + goto invalid; + } + } + + if (req->listen_addresses.num_listen_address > 0) { + struct rpc_listen_address *addr; + struct spdk_nvme_transport_id trid = {0}; + struct subsystem_listen_ctx *ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_nvmf_subsystem_destroy(subsystem); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "No Memory"); + free_rpc_subsystem(req); + return; + } + + ctx->req = req; + ctx->subsystem = subsystem; + ctx->request = request; + ctx->idx = 0; + + addr = &req->listen_addresses.addresses[0]; + + if (rpc_listen_address_to_trid(addr, &trid)) { + free(ctx); + goto invalid; + } + + spdk_nvmf_tgt_listen(g_spdk_nvmf_tgt, &trid, spdk_rpc_construct_subsystem_listen_done, ctx); + return; + } + + free_rpc_subsystem(req); + + spdk_nvmf_subsystem_start(subsystem, + spdk_rpc_nvmf_subsystem_started, + request); + + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_subsystem(req); +} +SPDK_RPC_REGISTER("construct_nvmf_subsystem", spdk_rpc_construct_nvmf_subsystem, SPDK_RPC_RUNTIME) diff --git a/src/spdk/lib/event/subsystems/nvmf/nvmf_tgt.c b/src/spdk/lib/event/subsystems/nvmf/nvmf_tgt.c new file mode 100644 index 00000000..bb35dcce --- /dev/null +++ b/src/spdk/lib/event/subsystems/nvmf/nvmf_tgt.c @@ -0,0 +1,503 @@ +/*- + * 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 "event_nvmf.h" + +#include "spdk/bdev.h" +#include "spdk/event.h" +#include "spdk/thread.h" +#include "spdk/log.h" +#include "spdk/nvme.h" +#include "spdk/util.h" + +enum nvmf_tgt_state { + NVMF_TGT_INIT_NONE = 0, + NVMF_TGT_INIT_PARSE_CONFIG, + NVMF_TGT_INIT_CREATE_POLL_GROUPS, + NVMF_TGT_INIT_START_SUBSYSTEMS, + NVMF_TGT_INIT_START_ACCEPTOR, + NVMF_TGT_RUNNING, + NVMF_TGT_FINI_STOP_SUBSYSTEMS, + NVMF_TGT_FINI_DESTROY_POLL_GROUPS, + NVMF_TGT_FINI_STOP_ACCEPTOR, + NVMF_TGT_FINI_FREE_RESOURCES, + NVMF_TGT_STOPPED, + NVMF_TGT_ERROR, +}; + +struct nvmf_tgt_poll_group { + struct spdk_nvmf_poll_group *group; +}; + +struct nvmf_tgt_host_trid { + struct spdk_nvme_transport_id host_trid; + uint32_t core; + uint32_t ref; + TAILQ_ENTRY(nvmf_tgt_host_trid) link; +}; + +/* List of host trids that are connected to the target */ +static TAILQ_HEAD(, nvmf_tgt_host_trid) g_nvmf_tgt_host_trids = + TAILQ_HEAD_INITIALIZER(g_nvmf_tgt_host_trids); + +struct spdk_nvmf_tgt *g_spdk_nvmf_tgt = NULL; + +static enum nvmf_tgt_state g_tgt_state; + +/* Round-Robin/IP-based tracking of cores for qpair assignment */ +static uint32_t g_tgt_core; + +static struct nvmf_tgt_poll_group *g_poll_groups = NULL; +static size_t g_num_poll_groups = 0; + +static struct spdk_poller *g_acceptor_poller = NULL; + +static void nvmf_tgt_advance_state(void); + +static void +_spdk_nvmf_shutdown_cb(void *arg1, void *arg2) +{ + /* Still in initialization state, defer shutdown operation */ + if (g_tgt_state < NVMF_TGT_RUNNING) { + spdk_event_call(spdk_event_allocate(spdk_env_get_current_core(), + _spdk_nvmf_shutdown_cb, NULL, NULL)); + return; + } else if (g_tgt_state > NVMF_TGT_RUNNING) { + /* Already in Shutdown status, ignore the signal */ + return; + } + + g_tgt_state = NVMF_TGT_FINI_STOP_SUBSYSTEMS; + nvmf_tgt_advance_state(); +} + +static void +spdk_nvmf_subsystem_fini(void) +{ + /* Always let the first core to handle the case */ + if (spdk_env_get_current_core() != spdk_env_get_first_core()) { + spdk_event_call(spdk_event_allocate(spdk_env_get_first_core(), + _spdk_nvmf_shutdown_cb, NULL, NULL)); + } else { + _spdk_nvmf_shutdown_cb(NULL, NULL); + } +} + +static void +nvmf_tgt_poll_group_add(void *arg1, void *arg2) +{ + struct spdk_nvmf_qpair *qpair = arg1; + struct nvmf_tgt_poll_group *pg = arg2; + + spdk_nvmf_poll_group_add(pg->group, qpair); +} + +/* Round robin selection of cores */ +static uint32_t +spdk_nvmf_get_core_rr(void) +{ + uint32_t core; + + core = g_tgt_core; + g_tgt_core = spdk_env_get_next_core(core); + if (g_tgt_core == UINT32_MAX) { + g_tgt_core = spdk_env_get_first_core(); + } + + return core; +} + +static void +nvmf_tgt_remove_host_trid(struct spdk_nvmf_qpair *qpair) +{ + struct spdk_nvme_transport_id trid_to_remove; + struct nvmf_tgt_host_trid *trid = NULL, *tmp_trid = NULL; + + if (g_spdk_nvmf_tgt_conf->conn_sched != CONNECT_SCHED_HOST_IP) { + return; + } + + if (spdk_nvmf_qpair_get_peer_trid(qpair, &trid_to_remove) != 0) { + return; + } + + TAILQ_FOREACH_SAFE(trid, &g_nvmf_tgt_host_trids, link, tmp_trid) { + if (trid && !strncmp(trid->host_trid.traddr, + trid_to_remove.traddr, SPDK_NVMF_TRADDR_MAX_LEN + 1)) { + trid->ref--; + if (trid->ref == 0) { + TAILQ_REMOVE(&g_nvmf_tgt_host_trids, trid, link); + free(trid); + } + + break; + } + } + + return; +} + +static uint32_t +nvmf_tgt_get_qpair_core(struct spdk_nvmf_qpair *qpair) +{ + struct spdk_nvme_transport_id trid; + struct nvmf_tgt_host_trid *tmp_trid = NULL, *new_trid = NULL; + int ret; + uint32_t core = 0; + + switch (g_spdk_nvmf_tgt_conf->conn_sched) { + case CONNECT_SCHED_HOST_IP: + ret = spdk_nvmf_qpair_get_peer_trid(qpair, &trid); + if (ret) { + SPDK_ERRLOG("Invalid host transport Id. Assigning to core %d\n", core); + break; + } + + TAILQ_FOREACH(tmp_trid, &g_nvmf_tgt_host_trids, link) { + if (tmp_trid && !strncmp(tmp_trid->host_trid.traddr, + trid.traddr, SPDK_NVMF_TRADDR_MAX_LEN + 1)) { + tmp_trid->ref++; + core = tmp_trid->core; + break; + } + } + if (!tmp_trid) { + new_trid = calloc(1, sizeof(*new_trid)); + if (!new_trid) { + SPDK_ERRLOG("Insufficient memory. Assigning to core %d\n", core); + break; + } + /* Get the next available core for the new host */ + core = spdk_nvmf_get_core_rr(); + new_trid->core = core; + memcpy(new_trid->host_trid.traddr, trid.traddr, + SPDK_NVMF_TRADDR_MAX_LEN + 1); + TAILQ_INSERT_TAIL(&g_nvmf_tgt_host_trids, new_trid, link); + } + break; + case CONNECT_SCHED_ROUND_ROBIN: + default: + core = spdk_nvmf_get_core_rr(); + break; + } + + return core; +} + +static void +new_qpair(struct spdk_nvmf_qpair *qpair) +{ + struct spdk_event *event; + struct nvmf_tgt_poll_group *pg; + uint32_t core; + uint32_t attempts; + + if (g_tgt_state != NVMF_TGT_RUNNING) { + spdk_nvmf_qpair_disconnect(qpair, NULL, NULL); + return; + } + + for (attempts = 0; attempts < g_num_poll_groups; attempts++) { + core = nvmf_tgt_get_qpair_core(qpair); + pg = &g_poll_groups[core]; + if (pg->group != NULL) { + break; + } else { + nvmf_tgt_remove_host_trid(qpair); + } + } + + if (attempts == g_num_poll_groups) { + SPDK_ERRLOG("No poll groups exist.\n"); + spdk_nvmf_qpair_disconnect(qpair, NULL, NULL); + return; + } + + event = spdk_event_allocate(core, nvmf_tgt_poll_group_add, qpair, pg); + spdk_event_call(event); +} + +static int +acceptor_poll(void *arg) +{ + struct spdk_nvmf_tgt *tgt = arg; + + spdk_nvmf_tgt_accept(tgt, new_qpair); + + return -1; +} + +static void +nvmf_tgt_destroy_poll_group_done(void *ctx) +{ + g_tgt_state = NVMF_TGT_FINI_STOP_ACCEPTOR; + nvmf_tgt_advance_state(); +} + +static void +nvmf_tgt_destroy_poll_group(void *ctx) +{ + struct nvmf_tgt_poll_group *pg; + + pg = &g_poll_groups[spdk_env_get_current_core()]; + + if (pg->group) { + spdk_nvmf_poll_group_destroy(pg->group); + pg->group = NULL; + } +} + +static void +nvmf_tgt_create_poll_group_done(void *ctx) +{ + g_tgt_state = NVMF_TGT_INIT_START_SUBSYSTEMS; + nvmf_tgt_advance_state(); +} + +static void +nvmf_tgt_create_poll_group(void *ctx) +{ + struct nvmf_tgt_poll_group *pg; + + pg = &g_poll_groups[spdk_env_get_current_core()]; + + pg->group = spdk_nvmf_poll_group_create(g_spdk_nvmf_tgt); +} + +static void +nvmf_tgt_subsystem_started(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + subsystem = spdk_nvmf_subsystem_get_next(subsystem); + + if (subsystem) { + spdk_nvmf_subsystem_start(subsystem, nvmf_tgt_subsystem_started, NULL); + return; + } + + g_tgt_state = NVMF_TGT_INIT_START_ACCEPTOR; + nvmf_tgt_advance_state(); +} + +static void +nvmf_tgt_subsystem_stopped(struct spdk_nvmf_subsystem *subsystem, + void *cb_arg, int status) +{ + subsystem = spdk_nvmf_subsystem_get_next(subsystem); + + if (subsystem) { + spdk_nvmf_subsystem_stop(subsystem, nvmf_tgt_subsystem_stopped, NULL); + return; + } + + g_tgt_state = NVMF_TGT_FINI_DESTROY_POLL_GROUPS; + nvmf_tgt_advance_state(); +} + +static void +nvmf_tgt_destroy_done(void *ctx, int status) +{ + struct nvmf_tgt_host_trid *trid, *tmp_trid; + + g_tgt_state = NVMF_TGT_STOPPED; + + TAILQ_FOREACH_SAFE(trid, &g_nvmf_tgt_host_trids, link, tmp_trid) { + TAILQ_REMOVE(&g_nvmf_tgt_host_trids, trid, link); + free(trid); + } + + free(g_spdk_nvmf_tgt_conf); + g_spdk_nvmf_tgt_conf = NULL; + nvmf_tgt_advance_state(); +} + +static void +nvmf_tgt_parse_conf_done(int status) +{ + g_tgt_state = (status == 0) ? NVMF_TGT_INIT_CREATE_POLL_GROUPS : NVMF_TGT_ERROR; + nvmf_tgt_advance_state(); +} + +static void +nvmf_tgt_parse_conf_start(void *ctx) +{ + if (spdk_nvmf_parse_conf(nvmf_tgt_parse_conf_done)) { + SPDK_ERRLOG("spdk_nvmf_parse_conf() failed\n"); + g_tgt_state = NVMF_TGT_ERROR; + nvmf_tgt_advance_state(); + } +} + +static void +nvmf_tgt_advance_state(void) +{ + enum nvmf_tgt_state prev_state; + int rc = -1; + + do { + prev_state = g_tgt_state; + + switch (g_tgt_state) { + case NVMF_TGT_INIT_NONE: { + g_tgt_state = NVMF_TGT_INIT_PARSE_CONFIG; + + /* Find the maximum core number */ + g_num_poll_groups = spdk_env_get_last_core() + 1; + assert(g_num_poll_groups > 0); + + g_poll_groups = calloc(g_num_poll_groups, sizeof(*g_poll_groups)); + if (g_poll_groups == NULL) { + g_tgt_state = NVMF_TGT_ERROR; + rc = -ENOMEM; + break; + } + + g_tgt_core = spdk_env_get_first_core(); + break; + } + case NVMF_TGT_INIT_PARSE_CONFIG: + /* Send message to self to call parse conf func. + * Prevents it from possibly performing cb before getting + * out of this function, which causes problems. */ + spdk_thread_send_msg(spdk_get_thread(), nvmf_tgt_parse_conf_start, NULL); + break; + case NVMF_TGT_INIT_CREATE_POLL_GROUPS: + /* Send a message to each thread and create a poll group */ + spdk_for_each_thread(nvmf_tgt_create_poll_group, + NULL, + nvmf_tgt_create_poll_group_done); + break; + case NVMF_TGT_INIT_START_SUBSYSTEMS: { + struct spdk_nvmf_subsystem *subsystem; + + subsystem = spdk_nvmf_subsystem_get_first(g_spdk_nvmf_tgt); + + if (subsystem) { + spdk_nvmf_subsystem_start(subsystem, nvmf_tgt_subsystem_started, NULL); + } else { + g_tgt_state = NVMF_TGT_INIT_START_ACCEPTOR; + } + break; + } + case NVMF_TGT_INIT_START_ACCEPTOR: + g_acceptor_poller = spdk_poller_register(acceptor_poll, g_spdk_nvmf_tgt, + g_spdk_nvmf_tgt_conf->acceptor_poll_rate); + SPDK_INFOLOG(SPDK_LOG_NVMF, "Acceptor running\n"); + g_tgt_state = NVMF_TGT_RUNNING; + break; + case NVMF_TGT_RUNNING: + spdk_subsystem_init_next(0); + break; + case NVMF_TGT_FINI_STOP_SUBSYSTEMS: { + struct spdk_nvmf_subsystem *subsystem; + + subsystem = spdk_nvmf_subsystem_get_first(g_spdk_nvmf_tgt); + + if (subsystem) { + spdk_nvmf_subsystem_stop(subsystem, nvmf_tgt_subsystem_stopped, NULL); + } else { + g_tgt_state = NVMF_TGT_FINI_DESTROY_POLL_GROUPS; + } + break; + } + case NVMF_TGT_FINI_DESTROY_POLL_GROUPS: + /* Send a message to each thread and destroy the poll group */ + spdk_for_each_thread(nvmf_tgt_destroy_poll_group, + NULL, + nvmf_tgt_destroy_poll_group_done); + break; + case NVMF_TGT_FINI_STOP_ACCEPTOR: + spdk_poller_unregister(&g_acceptor_poller); + g_tgt_state = NVMF_TGT_FINI_FREE_RESOURCES; + break; + case NVMF_TGT_FINI_FREE_RESOURCES: + spdk_nvmf_tgt_destroy(g_spdk_nvmf_tgt, nvmf_tgt_destroy_done, NULL); + break; + case NVMF_TGT_STOPPED: + spdk_subsystem_fini_next(); + return; + case NVMF_TGT_ERROR: + spdk_subsystem_init_next(rc); + return; + } + + } while (g_tgt_state != prev_state); +} + +static void +spdk_nvmf_subsystem_init(void) +{ + g_tgt_state = NVMF_TGT_INIT_NONE; + nvmf_tgt_advance_state(); +} + +static char * +get_conn_sched_string(enum spdk_nvmf_connect_sched sched) +{ + if (sched == CONNECT_SCHED_HOST_IP) { + return "hostip"; + } else { + return "roundrobin"; + } +} + +static void +spdk_nvmf_subsystem_write_config_json(struct spdk_json_write_ctx *w, struct spdk_event *done_ev) +{ + spdk_json_write_array_begin(w); + + spdk_json_write_object_begin(w); + spdk_json_write_named_string(w, "method", "set_nvmf_target_config"); + + spdk_json_write_named_object_begin(w, "params"); + spdk_json_write_named_uint32(w, "acceptor_poll_rate", g_spdk_nvmf_tgt_conf->acceptor_poll_rate); + spdk_json_write_named_string(w, "conn_sched", + get_conn_sched_string(g_spdk_nvmf_tgt_conf->conn_sched)); + spdk_json_write_object_end(w); + spdk_json_write_object_end(w); + + spdk_nvmf_tgt_write_config_json(w, g_spdk_nvmf_tgt); + spdk_json_write_array_end(w); + + spdk_event_call(done_ev); +} + +static struct spdk_subsystem g_spdk_subsystem_nvmf = { + .name = "nvmf", + .init = spdk_nvmf_subsystem_init, + .fini = spdk_nvmf_subsystem_fini, + .write_config_json = spdk_nvmf_subsystem_write_config_json, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_nvmf) +SPDK_SUBSYSTEM_DEPEND(nvmf, bdev) diff --git a/src/spdk/lib/event/subsystems/scsi/Makefile b/src/spdk/lib/event/subsystems/scsi/Makefile new file mode 100644 index 00000000..12bf15e3 --- /dev/null +++ b/src/spdk/lib/event/subsystems/scsi/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = scsi.c +LIBNAME = event_scsi + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/scsi/scsi.c b/src/spdk/lib/event/subsystems/scsi/scsi.c new file mode 100644 index 00000000..a37ebf61 --- /dev/null +++ b/src/spdk/lib/event/subsystems/scsi/scsi.c @@ -0,0 +1,65 @@ +/*- + * 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/scsi.h" + +#include "spdk_internal/event.h" + +static void +spdk_scsi_subsystem_init(void) +{ + int rc; + + rc = spdk_scsi_init(); + + spdk_subsystem_init_next(rc); +} + +static void +spdk_scsi_subsystem_fini(void) +{ + spdk_scsi_fini(); + spdk_subsystem_fini_next(); +} + +static struct spdk_subsystem g_spdk_subsystem_scsi = { + .name = "scsi", + .init = spdk_scsi_subsystem_init, + .fini = spdk_scsi_subsystem_fini, + .config = NULL, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_scsi); +SPDK_SUBSYSTEM_DEPEND(scsi, bdev) diff --git a/src/spdk/lib/event/subsystems/vhost/Makefile b/src/spdk/lib/event/subsystems/vhost/Makefile new file mode 100644 index 00000000..2e0d61fe --- /dev/null +++ b/src/spdk/lib/event/subsystems/vhost/Makefile @@ -0,0 +1,40 @@ +# +# 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 + +C_SRCS = vhost.c +LIBNAME = event_vhost + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/event/subsystems/vhost/vhost.c b/src/spdk/lib/event/subsystems/vhost/vhost.c new file mode 100644 index 00000000..1fdbc6aa --- /dev/null +++ b/src/spdk/lib/event/subsystems/vhost/vhost.c @@ -0,0 +1,71 @@ +/*- + * 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/vhost.h" + +#include "spdk_internal/event.h" + +static void +spdk_vhost_subsystem_init(void) +{ + int rc = 0; + + rc = spdk_vhost_init(); + + spdk_subsystem_init_next(rc); +} + +static void +spdk_vhost_subsystem_fini_done(void) +{ + spdk_subsystem_fini_next(); +} + +static void +spdk_vhost_subsystem_fini(void) +{ + spdk_vhost_fini(spdk_vhost_subsystem_fini_done); +} + +static struct spdk_subsystem g_spdk_subsystem_vhost = { + .name = "vhost", + .init = spdk_vhost_subsystem_init, + .fini = spdk_vhost_subsystem_fini, + .config = NULL, + .write_config_json = spdk_vhost_config_json, +}; + +SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_vhost); +SPDK_SUBSYSTEM_DEPEND(vhost, scsi) |