summaryrefslogtreecommitdiffstats
path: root/src/spdk/lib/nvme/nvme_fabric.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/lib/nvme/nvme_fabric.c')
-rw-r--r--src/spdk/lib/nvme/nvme_fabric.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/src/spdk/lib/nvme/nvme_fabric.c b/src/spdk/lib/nvme/nvme_fabric.c
new file mode 100644
index 000000000..9fff20873
--- /dev/null
+++ b/src/spdk/lib/nvme/nvme_fabric.c
@@ -0,0 +1,475 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation. All rights reserved.
+ * Copyright (c) 2020 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.
+ */
+
+/*
+ * NVMe over Fabrics transport-independent functions
+ */
+
+#include "nvme_internal.h"
+
+#include "spdk/endian.h"
+#include "spdk/string.h"
+
+static int
+nvme_fabric_prop_set_cmd(struct spdk_nvme_ctrlr *ctrlr,
+ uint32_t offset, uint8_t size, uint64_t value)
+{
+ struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
+ struct nvme_completion_poll_status *status;
+ int rc;
+
+ assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
+
+ status = calloc(1, sizeof(*status));
+ if (!status) {
+ SPDK_ERRLOG("Failed to allocate status tracker\n");
+ return -ENOMEM;
+ }
+
+ cmd.opcode = SPDK_NVME_OPC_FABRIC;
+ cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_SET;
+ cmd.ofst = offset;
+ cmd.attrib.size = size;
+ cmd.value.u64 = value;
+
+ rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
+ NULL, 0,
+ nvme_completion_poll_cb, status);
+ if (rc < 0) {
+ free(status);
+ return rc;
+ }
+
+ if (nvme_wait_for_completion(ctrlr->adminq, status)) {
+ if (!status->timed_out) {
+ free(status);
+ }
+ SPDK_ERRLOG("Property Set failed\n");
+ return -1;
+ }
+ free(status);
+
+ return 0;
+}
+
+static int
+nvme_fabric_prop_get_cmd(struct spdk_nvme_ctrlr *ctrlr,
+ uint32_t offset, uint8_t size, uint64_t *value)
+{
+ struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
+ struct nvme_completion_poll_status *status;
+ struct spdk_nvmf_fabric_prop_get_rsp *response;
+ int rc;
+
+ assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
+
+ status = calloc(1, sizeof(*status));
+ if (!status) {
+ SPDK_ERRLOG("Failed to allocate status tracker\n");
+ return -ENOMEM;
+ }
+
+ cmd.opcode = SPDK_NVME_OPC_FABRIC;
+ cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_GET;
+ cmd.ofst = offset;
+ cmd.attrib.size = size;
+
+ rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
+ NULL, 0, nvme_completion_poll_cb,
+ status);
+ if (rc < 0) {
+ free(status);
+ return rc;
+ }
+
+ if (nvme_wait_for_completion(ctrlr->adminq, status)) {
+ if (!status->timed_out) {
+ free(status);
+ }
+ SPDK_ERRLOG("Property Get failed\n");
+ return -1;
+ }
+
+ response = (struct spdk_nvmf_fabric_prop_get_rsp *)&status->cpl;
+
+ if (size == SPDK_NVMF_PROP_SIZE_4) {
+ *value = response->value.u32.low;
+ } else {
+ *value = response->value.u64;
+ }
+
+ free(status);
+
+ return 0;
+}
+
+int
+nvme_fabric_ctrlr_set_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value)
+{
+ return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, value);
+}
+
+int
+nvme_fabric_ctrlr_set_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value)
+{
+ return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
+}
+
+int
+nvme_fabric_ctrlr_get_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value)
+{
+ uint64_t tmp_value;
+ int rc;
+ rc = nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, &tmp_value);
+
+ if (!rc) {
+ *value = (uint32_t)tmp_value;
+ }
+ return rc;
+}
+
+int
+nvme_fabric_ctrlr_get_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value)
+{
+ return nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
+}
+
+static void
+nvme_fabric_discover_probe(struct spdk_nvmf_discovery_log_page_entry *entry,
+ struct spdk_nvme_probe_ctx *probe_ctx,
+ int discover_priority)
+{
+ struct spdk_nvme_transport_id trid;
+ uint8_t *end;
+ size_t len;
+
+ memset(&trid, 0, sizeof(trid));
+
+ if (entry->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) {
+ SPDK_WARNLOG("Skipping unsupported discovery service referral\n");
+ return;
+ } else if (entry->subtype != SPDK_NVMF_SUBTYPE_NVME) {
+ SPDK_WARNLOG("Skipping unknown subtype %u\n", entry->subtype);
+ return;
+ }
+
+ trid.trtype = entry->trtype;
+ spdk_nvme_transport_id_populate_trstring(&trid, spdk_nvme_transport_id_trtype_str(entry->trtype));
+ if (!spdk_nvme_transport_available_by_name(trid.trstring)) {
+ SPDK_WARNLOG("NVMe transport type %u not available; skipping probe\n",
+ trid.trtype);
+ return;
+ }
+
+ snprintf(trid.trstring, sizeof(trid.trstring), "%s", probe_ctx->trid.trstring);
+ trid.adrfam = entry->adrfam;
+
+ /* Ensure that subnqn is null terminated. */
+ end = memchr(entry->subnqn, '\0', SPDK_NVMF_NQN_MAX_LEN + 1);
+ if (!end) {
+ SPDK_ERRLOG("Discovery entry SUBNQN is not null terminated\n");
+ return;
+ }
+ len = end - entry->subnqn;
+ memcpy(trid.subnqn, entry->subnqn, len);
+ trid.subnqn[len] = '\0';
+
+ /* Convert traddr to a null terminated string. */
+ len = spdk_strlen_pad(entry->traddr, sizeof(entry->traddr), ' ');
+ memcpy(trid.traddr, entry->traddr, len);
+ if (spdk_str_chomp(trid.traddr) != 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_NVME, "Trailing newlines removed from discovery TRADDR\n");
+ }
+
+ /* Convert trsvcid to a null terminated string. */
+ len = spdk_strlen_pad(entry->trsvcid, sizeof(entry->trsvcid), ' ');
+ memcpy(trid.trsvcid, entry->trsvcid, len);
+ if (spdk_str_chomp(trid.trsvcid) != 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_NVME, "Trailing newlines removed from discovery TRSVCID\n");
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_NVME, "subnqn=%s, trtype=%u, traddr=%s, trsvcid=%s\n",
+ trid.subnqn, trid.trtype,
+ trid.traddr, trid.trsvcid);
+
+ /* Copy the priority from the discovery ctrlr */
+ trid.priority = discover_priority;
+
+ nvme_ctrlr_probe(&trid, probe_ctx, NULL);
+}
+
+static int
+nvme_fabric_get_discovery_log_page(struct spdk_nvme_ctrlr *ctrlr,
+ void *log_page, uint32_t size, uint64_t offset)
+{
+ struct nvme_completion_poll_status *status;
+ int rc;
+
+ status = calloc(1, sizeof(*status));
+ if (!status) {
+ SPDK_ERRLOG("Failed to allocate status tracker\n");
+ return -ENOMEM;
+ }
+
+ rc = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_NVME_LOG_DISCOVERY, 0, log_page, size, offset,
+ nvme_completion_poll_cb, status);
+ if (rc < 0) {
+ free(status);
+ return -1;
+ }
+
+ if (nvme_wait_for_completion(ctrlr->adminq, status)) {
+ if (!status->timed_out) {
+ free(status);
+ }
+ return -1;
+ }
+ free(status);
+
+ return 0;
+}
+
+int
+nvme_fabric_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx,
+ bool direct_connect)
+{
+ struct spdk_nvme_ctrlr_opts discovery_opts;
+ struct spdk_nvme_ctrlr *discovery_ctrlr;
+ union spdk_nvme_cc_register cc;
+ int rc;
+ struct nvme_completion_poll_status *status;
+
+ if (strcmp(probe_ctx->trid.subnqn, SPDK_NVMF_DISCOVERY_NQN) != 0) {
+ /* It is not a discovery_ctrlr info and try to directly connect it */
+ rc = nvme_ctrlr_probe(&probe_ctx->trid, probe_ctx, NULL);
+ return rc;
+ }
+
+ spdk_nvme_ctrlr_get_default_ctrlr_opts(&discovery_opts, sizeof(discovery_opts));
+ /* For discovery_ctrlr set the timeout to 0 */
+ discovery_opts.keep_alive_timeout_ms = 0;
+
+ discovery_ctrlr = nvme_transport_ctrlr_construct(&probe_ctx->trid, &discovery_opts, NULL);
+ if (discovery_ctrlr == NULL) {
+ return -1;
+ }
+ nvme_qpair_set_state(discovery_ctrlr->adminq, NVME_QPAIR_ENABLED);
+
+ /* TODO: this should be using the normal NVMe controller initialization process +1 */
+ cc.raw = 0;
+ cc.bits.en = 1;
+ cc.bits.iosqes = 6; /* SQ entry size == 64 == 2^6 */
+ cc.bits.iocqes = 4; /* CQ entry size == 16 == 2^4 */
+ rc = nvme_transport_ctrlr_set_reg_4(discovery_ctrlr, offsetof(struct spdk_nvme_registers, cc.raw),
+ cc.raw);
+ if (rc < 0) {
+ SPDK_ERRLOG("Failed to set cc\n");
+ nvme_ctrlr_destruct(discovery_ctrlr);
+ return -1;
+ }
+
+ status = calloc(1, sizeof(*status));
+ if (!status) {
+ SPDK_ERRLOG("Failed to allocate status tracker\n");
+ nvme_ctrlr_destruct(discovery_ctrlr);
+ return -ENOMEM;
+ }
+
+ /* get the cdata info */
+ rc = nvme_ctrlr_cmd_identify(discovery_ctrlr, SPDK_NVME_IDENTIFY_CTRLR, 0, 0,
+ &discovery_ctrlr->cdata, sizeof(discovery_ctrlr->cdata),
+ nvme_completion_poll_cb, status);
+ if (rc != 0) {
+ SPDK_ERRLOG("Failed to identify cdata\n");
+ nvme_ctrlr_destruct(discovery_ctrlr);
+ free(status);
+ return rc;
+ }
+
+ if (nvme_wait_for_completion(discovery_ctrlr->adminq, status)) {
+ SPDK_ERRLOG("nvme_identify_controller failed!\n");
+ nvme_ctrlr_destruct(discovery_ctrlr);
+ if (!status->timed_out) {
+ free(status);
+ }
+ return -ENXIO;
+ }
+
+ free(status);
+
+ /* Direct attach through spdk_nvme_connect() API */
+ if (direct_connect == true) {
+ /* Set the ready state to skip the normal init process */
+ discovery_ctrlr->state = NVME_CTRLR_STATE_READY;
+ nvme_ctrlr_connected(probe_ctx, discovery_ctrlr);
+ nvme_ctrlr_add_process(discovery_ctrlr, 0);
+ return 0;
+ }
+
+ rc = nvme_fabric_ctrlr_discover(discovery_ctrlr, probe_ctx);
+ nvme_ctrlr_destruct(discovery_ctrlr);
+ return rc;
+}
+
+int
+nvme_fabric_ctrlr_discover(struct spdk_nvme_ctrlr *ctrlr,
+ struct spdk_nvme_probe_ctx *probe_ctx)
+{
+ struct spdk_nvmf_discovery_log_page *log_page;
+ struct spdk_nvmf_discovery_log_page_entry *log_page_entry;
+ char buffer[4096];
+ int rc;
+ uint64_t i, numrec, buffer_max_entries_first, buffer_max_entries, log_page_offset = 0;
+ uint64_t remaining_num_rec = 0;
+ uint16_t recfmt;
+
+ memset(buffer, 0x0, 4096);
+ buffer_max_entries_first = (sizeof(buffer) - offsetof(struct spdk_nvmf_discovery_log_page,
+ entries[0])) /
+ sizeof(struct spdk_nvmf_discovery_log_page_entry);
+ buffer_max_entries = sizeof(buffer) / sizeof(struct spdk_nvmf_discovery_log_page_entry);
+ do {
+ rc = nvme_fabric_get_discovery_log_page(ctrlr, buffer, sizeof(buffer), log_page_offset);
+ if (rc < 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_NVME, "Get Log Page - Discovery error\n");
+ return rc;
+ }
+
+ if (!remaining_num_rec) {
+ log_page = (struct spdk_nvmf_discovery_log_page *)buffer;
+ recfmt = from_le16(&log_page->recfmt);
+ if (recfmt != 0) {
+ SPDK_ERRLOG("Unrecognized discovery log record format %" PRIu16 "\n", recfmt);
+ return -EPROTO;
+ }
+ remaining_num_rec = log_page->numrec;
+ log_page_offset = offsetof(struct spdk_nvmf_discovery_log_page, entries[0]);
+ log_page_entry = &log_page->entries[0];
+ numrec = spdk_min(remaining_num_rec, buffer_max_entries_first);
+ } else {
+ numrec = spdk_min(remaining_num_rec, buffer_max_entries);
+ log_page_entry = (struct spdk_nvmf_discovery_log_page_entry *)buffer;
+ }
+
+ for (i = 0; i < numrec; i++) {
+ nvme_fabric_discover_probe(log_page_entry++, probe_ctx, ctrlr->trid.priority);
+ }
+ remaining_num_rec -= numrec;
+ log_page_offset += numrec * sizeof(struct spdk_nvmf_discovery_log_page_entry);
+ } while (remaining_num_rec != 0);
+
+ return 0;
+}
+
+int
+nvme_fabric_qpair_connect(struct spdk_nvme_qpair *qpair, uint32_t num_entries)
+{
+ struct nvme_completion_poll_status *status;
+ struct spdk_nvmf_fabric_connect_rsp *rsp;
+ struct spdk_nvmf_fabric_connect_cmd cmd;
+ struct spdk_nvmf_fabric_connect_data *nvmf_data;
+ struct spdk_nvme_ctrlr *ctrlr;
+ int rc;
+
+ if (num_entries == 0 || num_entries > SPDK_NVME_IO_QUEUE_MAX_ENTRIES) {
+ return -EINVAL;
+ }
+
+ ctrlr = qpair->ctrlr;
+ if (!ctrlr) {
+ return -EINVAL;
+ }
+
+ nvmf_data = spdk_zmalloc(sizeof(*nvmf_data), 0, NULL,
+ SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
+ if (!nvmf_data) {
+ SPDK_ERRLOG("nvmf_data allocation error\n");
+ return -ENOMEM;
+ }
+
+ status = calloc(1, sizeof(*status));
+ if (!status) {
+ SPDK_ERRLOG("Failed to allocate status tracker\n");
+ spdk_free(nvmf_data);
+ return -ENOMEM;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = SPDK_NVME_OPC_FABRIC;
+ cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_CONNECT;
+ cmd.qid = qpair->id;
+ cmd.sqsize = num_entries - 1;
+ cmd.kato = ctrlr->opts.keep_alive_timeout_ms;
+
+ if (nvme_qpair_is_admin_queue(qpair)) {
+ nvmf_data->cntlid = 0xFFFF;
+ } else {
+ nvmf_data->cntlid = ctrlr->cntlid;
+ }
+
+ SPDK_STATIC_ASSERT(sizeof(nvmf_data->hostid) == sizeof(ctrlr->opts.extended_host_id),
+ "host ID size mismatch");
+ memcpy(nvmf_data->hostid, ctrlr->opts.extended_host_id, sizeof(nvmf_data->hostid));
+ snprintf(nvmf_data->hostnqn, sizeof(nvmf_data->hostnqn), "%s", ctrlr->opts.hostnqn);
+ snprintf(nvmf_data->subnqn, sizeof(nvmf_data->subnqn), "%s", ctrlr->trid.subnqn);
+
+ rc = spdk_nvme_ctrlr_cmd_io_raw(ctrlr, qpair,
+ (struct spdk_nvme_cmd *)&cmd,
+ nvmf_data, sizeof(*nvmf_data),
+ nvme_completion_poll_cb, status);
+ if (rc < 0) {
+ SPDK_ERRLOG("Connect command failed\n");
+ spdk_free(nvmf_data);
+ free(status);
+ return rc;
+ }
+
+ if (nvme_wait_for_completion(qpair, status)) {
+ SPDK_ERRLOG("Connect command failed\n");
+ spdk_free(nvmf_data);
+ if (!status->timed_out) {
+ free(status);
+ }
+ return -EIO;
+ }
+
+ if (nvme_qpair_is_admin_queue(qpair)) {
+ rsp = (struct spdk_nvmf_fabric_connect_rsp *)&status->cpl;
+ ctrlr->cntlid = rsp->status_code_specific.success.cntlid;
+ SPDK_DEBUGLOG(SPDK_LOG_NVME, "CNTLID 0x%04" PRIx16 "\n", ctrlr->cntlid);
+ }
+
+ spdk_free(nvmf_data);
+ free(status);
+ return 0;
+}