summaryrefslogtreecommitdiffstats
path: root/src/spdk/module/event/subsystems/nvmf/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/module/event/subsystems/nvmf/conf.c')
-rw-r--r--src/spdk/module/event/subsystems/nvmf/conf.c709
1 files changed, 709 insertions, 0 deletions
diff --git a/src/spdk/module/event/subsystems/nvmf/conf.c b/src/spdk/module/event/subsystems/nvmf/conf.c
new file mode 100644
index 000000000..b92a92acc
--- /dev/null
+++ b/src/spdk/module/event/subsystems/nvmf/conf.c
@@ -0,0 +1,709 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation. All rights reserved.
+ * Copyright (c) 2018 Mellanox Technologies LTD. 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_conf *g_spdk_nvmf_tgt_conf = NULL;
+uint32_t g_spdk_nvmf_tgt_max_subsystems = 0;
+
+static int
+nvmf_add_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
+nvmf_read_config_file_tgt_max_subsystems(struct spdk_conf_section *sp,
+ int *deprecated_values)
+{
+ int tgt_max_subsystems;
+ int deprecated;
+
+ tgt_max_subsystems = spdk_conf_section_get_intval(sp, "MaxSubsystems");
+ if (tgt_max_subsystems >= 0) {
+ g_spdk_nvmf_tgt_max_subsystems = tgt_max_subsystems;
+ }
+
+ deprecated = spdk_conf_section_get_intval(sp, "MaxQueueDepth");
+ if (deprecated >= 0) {
+ *deprecated_values = -1;
+ }
+
+ deprecated = spdk_conf_section_get_intval(sp, "MaxQueuesPerSession");
+ if (deprecated >= 0) {
+ *deprecated_values = -1;
+ }
+
+ deprecated = spdk_conf_section_get_intval(sp, "InCapsuleDataSize");
+ if (deprecated >= 0) {
+ *deprecated_values = -1;
+ }
+
+ deprecated = spdk_conf_section_get_intval(sp, "MaxIOSize");
+ if (deprecated >= 0) {
+ *deprecated_values = -1;
+ }
+
+ deprecated = spdk_conf_section_get_intval(sp, "IOUnitSize");
+ if (deprecated >= 0) {
+ *deprecated_values = -1;
+ }
+}
+
+static int
+nvmf_read_config_file_tgt_conf(struct spdk_conf_section *sp,
+ struct spdk_nvmf_tgt_conf *conf)
+{
+ int acceptor_poll_rate;
+ const char *conn_scheduler;
+ int rc = 0;
+
+ acceptor_poll_rate = spdk_conf_section_get_intval(sp, "AcceptorPollRate");
+ if (acceptor_poll_rate >= 0) {
+ conf->acceptor_poll_rate = acceptor_poll_rate;
+ }
+
+ conn_scheduler = spdk_conf_section_get_val(sp, "ConnectionScheduler");
+
+ if (conn_scheduler) {
+ SPDK_NOTICELOG("The ConnectionScheduler option is no longer valid. Ignoring it.\n");
+ }
+
+ conf->admin_passthru.identify_ctrlr = spdk_conf_section_get_boolval(sp,
+ "AdminCmdPassthruIdentifyCtrlr", false);
+
+ return rc;
+}
+
+static int
+nvmf_parse_tgt_max_subsystems(void)
+{
+ struct spdk_conf_section *sp;
+ int deprecated_values = 0;
+
+ sp = spdk_conf_find_section(NULL, "Nvmf");
+ if (sp != NULL) {
+ nvmf_read_config_file_tgt_max_subsystems(sp, &deprecated_values);
+ }
+
+ return deprecated_values;
+}
+
+static struct spdk_nvmf_tgt_conf *
+nvmf_parse_tgt_conf(void)
+{
+ struct spdk_nvmf_tgt_conf *conf;
+ struct spdk_conf_section *sp;
+ int rc;
+
+ 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->admin_passthru.identify_ctrlr = false;
+
+ sp = spdk_conf_find_section(NULL, "Nvmf");
+ if (sp != NULL) {
+ rc = nvmf_read_config_file_tgt_conf(sp, conf);
+ if (rc) {
+ free(conf);
+ return NULL;
+ }
+ }
+
+ return conf;
+}
+
+static int
+nvmf_parse_nvmf_tgt(void)
+{
+ int rc;
+ int using_deprecated_options;
+ struct spdk_nvmf_target_opts opts = {
+ .name = "nvmf_tgt",
+ .max_subsystems = 0
+ };
+
+ if (!g_spdk_nvmf_tgt_max_subsystems) {
+ using_deprecated_options = nvmf_parse_tgt_max_subsystems();
+ if (using_deprecated_options < 0) {
+ SPDK_ERRLOG("Deprecated options detected for the NVMe-oF target.\n"
+ "The following options are no longer controlled by the target\n"
+ "and should be set in the transport on a per-transport basis:\n"
+ "MaxQueueDepth, MaxQueuesPerSession, InCapsuleDataSize, MaxIOSize, IOUnitSize\n"
+ "This can be accomplished by setting the options through the create_nvmf_transport RPC.\n"
+ "You may also continue to configure these options in the conf file under each transport.");
+ }
+ }
+
+ if (!g_spdk_nvmf_tgt_conf) {
+ g_spdk_nvmf_tgt_conf = nvmf_parse_tgt_conf();
+ if (!g_spdk_nvmf_tgt_conf) {
+ SPDK_ERRLOG("nvmf_parse_tgt_conf() failed\n");
+ return -1;
+ }
+ }
+
+ opts.max_subsystems = g_spdk_nvmf_tgt_max_subsystems;
+ g_spdk_nvmf_tgt = spdk_nvmf_tgt_create(&opts);
+
+ g_spdk_nvmf_tgt_max_subsystems = 0;
+
+ if (!g_spdk_nvmf_tgt) {
+ SPDK_ERRLOG("spdk_nvmf_tgt_create() failed\n");
+ return -1;
+ }
+
+ rc = nvmf_add_discovery_subsystem();
+ if (rc != 0) {
+ SPDK_ERRLOG("nvmf_add_discovery_subsystem failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+nvmf_tgt_parse_listen_ip_addr(char *address,
+ struct spdk_nvme_transport_id *trid)
+{
+ char *host;
+ char *port;
+
+ if (spdk_parse_ip_addr(address, &host, &port) < 0) {
+ SPDK_ERRLOG("Unable to parse listen address '%s'\n", address);
+ return -1;
+ }
+
+ 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);
+ }
+
+ return 0;
+}
+
+static int
+nvmf_tgt_parse_listen_fc_addr(const char *address,
+ struct spdk_nvme_transport_id *trid)
+{
+ /* transport address format and requirements,
+ * "nn-0xWWNN:pn-0xWWPN" - size equals 43 bytes and is required to
+ * contain 'nn' and 'pn'.
+ */
+ if (strlen(address) != 43 || strncmp(address, "nn-0x", 5) ||
+ strncmp(&address[21], ":pn-0x", 6)) {
+ SPDK_ERRLOG("Unable to parse fc address '%s'\n", address);
+ return -1;
+ }
+
+ trid->adrfam = SPDK_NVMF_ADRFAM_FC;
+ snprintf(trid->trsvcid, sizeof(trid->trsvcid), "none");
+ snprintf(trid->traddr, sizeof(trid->traddr), "%s", address);
+
+ return 0;
+}
+
+static void
+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
+nvmf_parse_subsystem(struct spdk_conf_section *sp)
+{
+ const char *nqn, *mode;
+ size_t i;
+ int ret = -1;
+ int lcore;
+ bool allow_any_host;
+ bool allow_any_listener = true;
+ const char *sn;
+ const char *mn;
+ 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;
+ }
+
+ mn = spdk_conf_section_get_val(sp, "MN");
+ if (mn == NULL) {
+ SPDK_NOTICELOG(
+ "Subsystem %s: missing model number, will use default\n",
+ nqn);
+ }
+
+ if (mn != NULL) {
+ if (spdk_nvmf_subsystem_set_mn(subsystem, mn)) {
+ SPDK_ERRLOG("Subsystem %s: invalid model number '%s'\n", nqn, mn);
+ 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), NULL) == 0) {
+ SPDK_ERRLOG("Unable to add namespace\n");
+ spdk_nvmf_subsystem_destroy(subsystem);
+ subsystem = NULL;
+ goto done;
+ }
+ }
+
+ /* Parse Listen sections */
+ for (i = 0; ; i++) {
+ struct spdk_nvme_transport_id trid = {{0}};
+ const char *transport;
+ const char *address;
+ char *address_dup;
+
+ transport = spdk_conf_section_get_nmval(sp, "Listen", i, 0);
+ if (!transport) {
+ break;
+ }
+
+ if (spdk_nvme_transport_id_populate_trstring(&trid, transport)) {
+ SPDK_ERRLOG("Invalid listen address transport type '%s'\n", transport);
+ continue;
+ }
+
+ 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;
+ }
+
+ if (trid.trtype == SPDK_NVME_TRANSPORT_RDMA ||
+ trid.trtype == SPDK_NVME_TRANSPORT_TCP) {
+ ret = nvmf_tgt_parse_listen_ip_addr(address_dup, &trid);
+ } else if (trid.trtype == SPDK_NVME_TRANSPORT_FC) {
+ ret = nvmf_tgt_parse_listen_fc_addr(address_dup, &trid);
+ }
+
+ free(address_dup);
+
+ if (ret) {
+ continue;
+ }
+
+ if (spdk_nvmf_tgt_listen(g_spdk_nvmf_tgt, &trid)) {
+ SPDK_ERRLOG("Failed to listen on transport address\n");
+ }
+
+ spdk_nvmf_subsystem_add_listener(subsystem, &trid, nvmf_tgt_listen_done, NULL);
+ allow_any_listener = false;
+ }
+
+ spdk_nvmf_subsystem_allow_any_listener(subsystem, allow_any_listener);
+
+ /* 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) ? 0 : -1;
+}
+
+static int
+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 = nvmf_parse_subsystem(sp);
+ if (rc < 0) {
+ return -1;
+ }
+ }
+ sp = spdk_conf_next_section(sp);
+ }
+ return 0;
+}
+
+struct nvmf_parse_transport_ctx {
+ struct spdk_conf_section *sp;
+ nvmf_parse_conf_done_fn cb_fn;
+};
+
+static void nvmf_parse_transport(struct nvmf_parse_transport_ctx *ctx);
+
+static void
+nvmf_tgt_add_transport_done(void *cb_arg, int status)
+{
+ struct 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")) {
+ nvmf_parse_transport(ctx);
+ return;
+ }
+ ctx->sp = spdk_conf_next_section(ctx->sp);
+ }
+
+ /* done with transports, parse Subsystem sections */
+ rc = nvmf_parse_subsystems();
+
+ ctx->cb_fn(rc);
+ free(ctx);
+}
+
+static void
+nvmf_parse_transport(struct 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;
+ bool bval;
+ 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;
+ }
+
+ spdk_nvme_transport_id_parse_trtype(&trtype, type);
+
+ if (spdk_nvmf_tgt_get_transport(g_spdk_nvmf_tgt, type)) {
+ SPDK_ERRLOG("Duplicate transport type '%s'\n", type);
+ ctx->cb_fn(-1);
+ free(ctx);
+ return;
+ }
+
+ if (!spdk_nvmf_transport_opts_init(type, &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;
+ }
+ val = spdk_conf_section_get_intval(ctx->sp, "NumSharedBuffers");
+ if (val >= 0) {
+ opts.num_shared_buffers = val;
+ }
+ val = spdk_conf_section_get_intval(ctx->sp, "BufCacheSize");
+ if (val >= 0) {
+ opts.buf_cache_size = val;
+ }
+
+ if (trtype == SPDK_NVME_TRANSPORT_RDMA) {
+ val = spdk_conf_section_get_intval(ctx->sp, "MaxSRQDepth");
+ if (val >= 0) {
+ opts.max_srq_depth = val;
+ }
+ bval = spdk_conf_section_get_boolval(ctx->sp, "NoSRQ", false);
+ opts.no_srq = bval;
+ }
+
+ if (trtype == SPDK_NVME_TRANSPORT_TCP) {
+ bval = spdk_conf_section_get_boolval(ctx->sp, "C2HSuccess", true);
+ opts.c2h_success = bval;
+
+ val = spdk_conf_section_get_intval(ctx->sp, "SockPriority");
+ if (val >= 0) {
+ opts.sock_priority = val;
+ }
+ }
+
+ bval = spdk_conf_section_get_boolval(ctx->sp, "DifInsertOrStrip", false);
+ opts.dif_insert_or_strip = bval;
+
+ transport = spdk_nvmf_transport_create(type, &opts);
+ if (transport) {
+ spdk_nvmf_tgt_add_transport(g_spdk_nvmf_tgt, transport, nvmf_tgt_add_transport_done, ctx);
+ } else {
+ goto error_out;
+ }
+
+ return;
+
+error_out:
+ ctx->cb_fn(-1);
+ free(ctx);
+ return;
+}
+
+static int
+nvmf_parse_transports(nvmf_parse_conf_done_fn cb_fn)
+{
+ struct nvmf_parse_transport_ctx *ctx;
+
+ ctx = calloc(1, sizeof(struct 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);
+ if (ctx->sp == NULL) {
+ free(ctx);
+ cb_fn(0);
+
+ return 0;
+ }
+
+ while (ctx->sp != NULL) {
+ if (spdk_conf_section_match_prefix(ctx->sp, "Transport")) {
+ 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(0);
+ return 0;
+}
+
+int
+nvmf_parse_conf(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 = nvmf_parse_nvmf_tgt();
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* Transport sections */
+ rc = nvmf_parse_transports(cb_fn);
+ if (rc < 0) {
+ return rc;
+ }
+
+ return 0;
+}