summaryrefslogtreecommitdiffstats
path: root/src/spdk/lib/event
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/lib/event')
-rw-r--r--src/spdk/lib/event/Makefile42
-rw-r--r--src/spdk/lib/event/app.c998
-rw-r--r--src/spdk/lib/event/reactor.c804
-rw-r--r--src/spdk/lib/event/rpc.c82
-rw-r--r--src/spdk/lib/event/rpc/Makefile40
-rw-r--r--src/spdk/lib/event/rpc/app_rpc.c155
-rw-r--r--src/spdk/lib/event/rpc/subsystem_rpc.c129
-rw-r--r--src/spdk/lib/event/subsystem.c256
-rw-r--r--src/spdk/lib/event/subsystems/Makefile44
-rw-r--r--src/spdk/lib/event/subsystems/bdev/Makefile40
-rw-r--r--src/spdk/lib/event/subsystems/bdev/bdev.c83
-rw-r--r--src/spdk/lib/event/subsystems/bdev/bdev_rpc.c97
-rw-r--r--src/spdk/lib/event/subsystems/copy/Makefile40
-rw-r--r--src/spdk/lib/event/subsystems/copy/copy.c70
-rw-r--r--src/spdk/lib/event/subsystems/iscsi/Makefile41
-rw-r--r--src/spdk/lib/event/subsystems/iscsi/iscsi.c81
-rw-r--r--src/spdk/lib/event/subsystems/iscsi/iscsi_rpc.c119
-rw-r--r--src/spdk/lib/event/subsystems/nbd/Makefile40
-rw-r--r--src/spdk/lib/event/subsystems/nbd/nbd.c74
-rw-r--r--src/spdk/lib/event/subsystems/net/Makefile40
-rw-r--r--src/spdk/lib/event/subsystems/net/net.c91
-rw-r--r--src/spdk/lib/event/subsystems/nvmf/Makefile40
-rw-r--r--src/spdk/lib/event/subsystems/nvmf/conf.c587
-rw-r--r--src/spdk/lib/event/subsystems/nvmf/event_nvmf.h67
-rw-r--r--src/spdk/lib/event/subsystems/nvmf/nvmf_rpc.c1562
-rw-r--r--src/spdk/lib/event/subsystems/nvmf/nvmf_rpc_deprecated.c620
-rw-r--r--src/spdk/lib/event/subsystems/nvmf/nvmf_tgt.c503
-rw-r--r--src/spdk/lib/event/subsystems/scsi/Makefile40
-rw-r--r--src/spdk/lib/event/subsystems/scsi/scsi.c65
-rw-r--r--src/spdk/lib/event/subsystems/vhost/Makefile40
-rw-r--r--src/spdk/lib/event/subsystems/vhost/vhost.c71
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)