b50c6bc2d9
Currently we are iterating over a hwqp connection list for every IO command received. With high load of connections, this is causing penalty. Use hash table for connection lookup based on connection ID and also RPI identifier. Signed-off-by: Naresh Gottumukkala <raju.gottumukkala@broadcom.com> Change-Id: I857e299722a0b72b25b0dbfe646d446ad98b7c76 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/5688 Community-CI: Broadcom CI Community-CI: Mellanox Build Bot Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
3675 lines
99 KiB
C
3675 lines
99 KiB
C
/*
|
|
* BSD LICENSE
|
|
*
|
|
* Copyright (c) 2018-2019 Broadcom. All Rights Reserved.
|
|
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
*
|
|
* 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_FC transport functions.
|
|
*/
|
|
|
|
#include "spdk/env.h"
|
|
#include "spdk/assert.h"
|
|
#include "spdk/nvmf_transport.h"
|
|
#include "spdk/string.h"
|
|
#include "spdk/trace.h"
|
|
#include "spdk/util.h"
|
|
#include "spdk/likely.h"
|
|
#include "spdk/endian.h"
|
|
#include "spdk/log.h"
|
|
#include "spdk/thread.h"
|
|
|
|
#include "nvmf_fc.h"
|
|
#include "fc_lld.h"
|
|
|
|
#ifndef DEV_VERIFY
|
|
#define DEV_VERIFY assert
|
|
#endif
|
|
|
|
#ifndef ASSERT_SPDK_FC_MAIN_THREAD
|
|
#define ASSERT_SPDK_FC_MAIN_THREAD() \
|
|
DEV_VERIFY(spdk_get_thread() == nvmf_fc_get_main_thread());
|
|
#endif
|
|
|
|
/*
|
|
* PRLI service parameters
|
|
*/
|
|
enum spdk_nvmf_fc_service_parameters {
|
|
SPDK_NVMF_FC_FIRST_BURST_SUPPORTED = 0x0001,
|
|
SPDK_NVMF_FC_DISCOVERY_SERVICE = 0x0008,
|
|
SPDK_NVMF_FC_TARGET_FUNCTION = 0x0010,
|
|
SPDK_NVMF_FC_INITIATOR_FUNCTION = 0x0020,
|
|
SPDK_NVMF_FC_CONFIRMED_COMPLETION_SUPPORTED = 0x0080,
|
|
};
|
|
|
|
static char *fc_req_state_strs[] = {
|
|
"SPDK_NVMF_FC_REQ_INIT",
|
|
"SPDK_NVMF_FC_REQ_READ_BDEV",
|
|
"SPDK_NVMF_FC_REQ_READ_XFER",
|
|
"SPDK_NVMF_FC_REQ_READ_RSP",
|
|
"SPDK_NVMF_FC_REQ_WRITE_BUFFS",
|
|
"SPDK_NVMF_FC_REQ_WRITE_XFER",
|
|
"SPDK_NVMF_FC_REQ_WRITE_BDEV",
|
|
"SPDK_NVMF_FC_REQ_WRITE_RSP",
|
|
"SPDK_NVMF_FC_REQ_NONE_BDEV",
|
|
"SPDK_NVMF_FC_REQ_NONE_RSP",
|
|
"SPDK_NVMF_FC_REQ_SUCCESS",
|
|
"SPDK_NVMF_FC_REQ_FAILED",
|
|
"SPDK_NVMF_FC_REQ_ABORTED",
|
|
"SPDK_NVMF_FC_REQ_BDEV_ABORTED",
|
|
"SPDK_NVMF_FC_REQ_PENDING"
|
|
};
|
|
|
|
#define OBJECT_NVMF_FC_IO 0xA0
|
|
|
|
#define TRACE_GROUP_NVMF_FC 0x8
|
|
#define TRACE_FC_REQ_INIT SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x01)
|
|
#define TRACE_FC_REQ_READ_BDEV SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x02)
|
|
#define TRACE_FC_REQ_READ_XFER SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x03)
|
|
#define TRACE_FC_REQ_READ_RSP SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x04)
|
|
#define TRACE_FC_REQ_WRITE_BUFFS SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x05)
|
|
#define TRACE_FC_REQ_WRITE_XFER SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x06)
|
|
#define TRACE_FC_REQ_WRITE_BDEV SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x07)
|
|
#define TRACE_FC_REQ_WRITE_RSP SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x08)
|
|
#define TRACE_FC_REQ_NONE_BDEV SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x09)
|
|
#define TRACE_FC_REQ_NONE_RSP SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x0A)
|
|
#define TRACE_FC_REQ_SUCCESS SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x0B)
|
|
#define TRACE_FC_REQ_FAILED SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x0C)
|
|
#define TRACE_FC_REQ_ABORTED SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x0D)
|
|
#define TRACE_FC_REQ_BDEV_ABORTED SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x0E)
|
|
#define TRACE_FC_REQ_PENDING SPDK_TPOINT_ID(TRACE_GROUP_NVMF_FC, 0x0F)
|
|
|
|
#define HWQP_CONN_TABLE_SIZE 8192
|
|
#define HWQP_RPI_TABLE_SIZE 4096
|
|
|
|
SPDK_TRACE_REGISTER_FN(nvmf_fc_trace, "nvmf_fc", TRACE_GROUP_NVMF_FC)
|
|
{
|
|
spdk_trace_register_object(OBJECT_NVMF_FC_IO, 'r');
|
|
spdk_trace_register_description("FC_REQ_NEW",
|
|
TRACE_FC_REQ_INIT,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 1, 1, "");
|
|
spdk_trace_register_description("FC_REQ_READ_SUBMIT_TO_BDEV",
|
|
TRACE_FC_REQ_READ_BDEV,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_READ_XFER_DATA",
|
|
TRACE_FC_REQ_READ_XFER,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_READ_RSP",
|
|
TRACE_FC_REQ_READ_RSP,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_WRITE_NEED_BUFFER",
|
|
TRACE_FC_REQ_WRITE_BUFFS,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_WRITE_XFER_DATA",
|
|
TRACE_FC_REQ_WRITE_XFER,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_WRITE_SUBMIT_TO_BDEV",
|
|
TRACE_FC_REQ_WRITE_BDEV,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_WRITE_RSP",
|
|
TRACE_FC_REQ_WRITE_RSP,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_NONE_SUBMIT_TO_BDEV",
|
|
TRACE_FC_REQ_NONE_BDEV,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_NONE_RSP",
|
|
TRACE_FC_REQ_NONE_RSP,
|
|
OWNER_NONE, OBJECT_NVMF_FC_IO, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_SUCCESS",
|
|
TRACE_FC_REQ_SUCCESS,
|
|
OWNER_NONE, OBJECT_NONE, 0, 0, "");
|
|
spdk_trace_register_description("FC_REQ_FAILED",
|
|
TRACE_FC_REQ_FAILED,
|
|
OWNER_NONE, OBJECT_NONE, 0, 0, "");
|
|
spdk_trace_register_description("FC_REQ_ABORTED",
|
|
TRACE_FC_REQ_ABORTED,
|
|
OWNER_NONE, OBJECT_NONE, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_ABORTED_SUBMIT_TO_BDEV",
|
|
TRACE_FC_REQ_BDEV_ABORTED,
|
|
OWNER_NONE, OBJECT_NONE, 0, 1, "");
|
|
spdk_trace_register_description("FC_REQ_PENDING",
|
|
TRACE_FC_REQ_PENDING,
|
|
OWNER_NONE, OBJECT_NONE, 0, 1, "");
|
|
}
|
|
|
|
/**
|
|
* The structure used by all fc adm functions
|
|
*/
|
|
struct spdk_nvmf_fc_adm_api_data {
|
|
void *api_args;
|
|
spdk_nvmf_fc_callback cb_func;
|
|
};
|
|
|
|
/**
|
|
* The callback structure for nport-delete
|
|
*/
|
|
struct spdk_nvmf_fc_adm_nport_del_cb_data {
|
|
struct spdk_nvmf_fc_nport *nport;
|
|
uint8_t port_handle;
|
|
spdk_nvmf_fc_callback fc_cb_func;
|
|
void *fc_cb_ctx;
|
|
};
|
|
|
|
/**
|
|
* The callback structure for it-delete
|
|
*/
|
|
struct spdk_nvmf_fc_adm_i_t_del_cb_data {
|
|
struct spdk_nvmf_fc_nport *nport;
|
|
struct spdk_nvmf_fc_remote_port_info *rport;
|
|
uint8_t port_handle;
|
|
spdk_nvmf_fc_callback fc_cb_func;
|
|
void *fc_cb_ctx;
|
|
};
|
|
|
|
|
|
typedef void (*spdk_nvmf_fc_adm_i_t_delete_assoc_cb_fn)(void *arg, uint32_t err);
|
|
|
|
/**
|
|
* The callback structure for the it-delete-assoc callback
|
|
*/
|
|
struct spdk_nvmf_fc_adm_i_t_del_assoc_cb_data {
|
|
struct spdk_nvmf_fc_nport *nport;
|
|
struct spdk_nvmf_fc_remote_port_info *rport;
|
|
uint8_t port_handle;
|
|
spdk_nvmf_fc_adm_i_t_delete_assoc_cb_fn cb_func;
|
|
void *cb_ctx;
|
|
};
|
|
|
|
/*
|
|
* Call back function pointer for HW port quiesce.
|
|
*/
|
|
typedef void (*spdk_nvmf_fc_adm_hw_port_quiesce_cb_fn)(void *ctx, int err);
|
|
|
|
/**
|
|
* Context structure for quiescing a hardware port
|
|
*/
|
|
struct spdk_nvmf_fc_adm_hw_port_quiesce_ctx {
|
|
int quiesce_count;
|
|
void *ctx;
|
|
spdk_nvmf_fc_adm_hw_port_quiesce_cb_fn cb_func;
|
|
};
|
|
|
|
/**
|
|
* Context structure used to reset a hardware port
|
|
*/
|
|
struct spdk_nvmf_fc_adm_hw_port_reset_ctx {
|
|
void *reset_args;
|
|
spdk_nvmf_fc_callback reset_cb_func;
|
|
};
|
|
|
|
struct spdk_nvmf_fc_transport {
|
|
struct spdk_nvmf_transport transport;
|
|
pthread_mutex_t lock;
|
|
};
|
|
|
|
static struct spdk_nvmf_fc_transport *g_nvmf_ftransport;
|
|
|
|
static TAILQ_HEAD(, spdk_nvmf_fc_port) g_spdk_nvmf_fc_port_list =
|
|
TAILQ_HEAD_INITIALIZER(g_spdk_nvmf_fc_port_list);
|
|
|
|
static struct spdk_thread *g_nvmf_fc_main_thread = NULL;
|
|
|
|
static uint32_t g_nvmf_fgroup_count = 0;
|
|
static TAILQ_HEAD(, spdk_nvmf_fc_poll_group) g_nvmf_fgroups =
|
|
TAILQ_HEAD_INITIALIZER(g_nvmf_fgroups);
|
|
|
|
struct spdk_thread *
|
|
nvmf_fc_get_main_thread(void)
|
|
{
|
|
return g_nvmf_fc_main_thread;
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_record_req_trace_point(struct spdk_nvmf_fc_request *fc_req,
|
|
enum spdk_nvmf_fc_request_state state)
|
|
{
|
|
uint16_t tpoint_id = SPDK_TRACE_MAX_TPOINT_ID;
|
|
|
|
switch (state) {
|
|
case SPDK_NVMF_FC_REQ_INIT:
|
|
/* Start IO tracing */
|
|
tpoint_id = TRACE_FC_REQ_INIT;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_READ_BDEV:
|
|
tpoint_id = TRACE_FC_REQ_READ_BDEV;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_READ_XFER:
|
|
tpoint_id = TRACE_FC_REQ_READ_XFER;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_READ_RSP:
|
|
tpoint_id = TRACE_FC_REQ_READ_RSP;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_WRITE_BUFFS:
|
|
tpoint_id = TRACE_FC_REQ_WRITE_BUFFS;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_WRITE_XFER:
|
|
tpoint_id = TRACE_FC_REQ_WRITE_XFER;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_WRITE_BDEV:
|
|
tpoint_id = TRACE_FC_REQ_WRITE_BDEV;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_WRITE_RSP:
|
|
tpoint_id = TRACE_FC_REQ_WRITE_RSP;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_NONE_BDEV:
|
|
tpoint_id = TRACE_FC_REQ_NONE_BDEV;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_NONE_RSP:
|
|
tpoint_id = TRACE_FC_REQ_NONE_RSP;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_SUCCESS:
|
|
tpoint_id = TRACE_FC_REQ_SUCCESS;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_FAILED:
|
|
tpoint_id = TRACE_FC_REQ_FAILED;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_ABORTED:
|
|
tpoint_id = TRACE_FC_REQ_ABORTED;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_BDEV_ABORTED:
|
|
tpoint_id = TRACE_FC_REQ_ABORTED;
|
|
break;
|
|
case SPDK_NVMF_FC_REQ_PENDING:
|
|
tpoint_id = TRACE_FC_REQ_PENDING;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
if (tpoint_id != SPDK_TRACE_MAX_TPOINT_ID) {
|
|
spdk_trace_record(tpoint_id, fc_req->poller_lcore, 0,
|
|
(uint64_t)(&fc_req->req), 0);
|
|
}
|
|
}
|
|
|
|
static struct rte_hash *
|
|
nvmf_fc_create_hash_table(const char *name, size_t num_entries, size_t key_len)
|
|
{
|
|
struct rte_hash_parameters hash_params = { 0 };
|
|
|
|
hash_params.entries = num_entries;
|
|
hash_params.key_len = key_len;
|
|
hash_params.name = name;
|
|
|
|
return rte_hash_create(&hash_params);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_handle_connection_failure(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn = arg;
|
|
struct spdk_nvmf_fc_ls_add_conn_api_data *api_data = NULL;
|
|
|
|
if (!fc_conn->create_opd) {
|
|
return;
|
|
}
|
|
api_data = &fc_conn->create_opd->u.add_conn;
|
|
|
|
nvmf_fc_ls_add_conn_failure(api_data->assoc, api_data->ls_rqst,
|
|
api_data->args.fc_conn, api_data->aq_conn);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_handle_assoc_deletion(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn = arg;
|
|
|
|
nvmf_fc_delete_association(fc_conn->fc_assoc->tgtport,
|
|
fc_conn->fc_assoc->assoc_id, false, true, NULL, NULL);
|
|
}
|
|
|
|
void
|
|
nvmf_fc_free_conn_reqpool(struct spdk_nvmf_fc_conn *fc_conn)
|
|
{
|
|
free(fc_conn->pool_memory);
|
|
fc_conn->pool_memory = NULL;
|
|
}
|
|
|
|
int
|
|
nvmf_fc_create_conn_reqpool(struct spdk_nvmf_fc_conn *fc_conn)
|
|
{
|
|
uint32_t i, qd;
|
|
struct spdk_nvmf_fc_pooled_request *req;
|
|
|
|
/*
|
|
* Create number of fc-requests to be more than the actual SQ size.
|
|
* This is to handle race conditions where the target driver may send
|
|
* back a RSP and before the target driver gets to process the CQE
|
|
* for the RSP, the initiator may have sent a new command.
|
|
* Depending on the load on the HWQP, there is a slim possibility
|
|
* that the target reaps the RQE corresponding to the new
|
|
* command before processing the CQE corresponding to the RSP.
|
|
*/
|
|
qd = fc_conn->max_queue_depth * 2;
|
|
|
|
STAILQ_INIT(&fc_conn->pool_queue);
|
|
fc_conn->pool_memory = calloc((fc_conn->max_queue_depth * 2),
|
|
sizeof(struct spdk_nvmf_fc_request));
|
|
if (!fc_conn->pool_memory) {
|
|
SPDK_ERRLOG("create fc req ring objects failed\n");
|
|
goto error;
|
|
}
|
|
fc_conn->pool_size = qd;
|
|
fc_conn->pool_free_elems = qd;
|
|
|
|
/* Initialise value in ring objects and link the objects */
|
|
for (i = 0; i < qd; i++) {
|
|
req = (struct spdk_nvmf_fc_pooled_request *)((char *)fc_conn->pool_memory +
|
|
i * sizeof(struct spdk_nvmf_fc_request));
|
|
|
|
STAILQ_INSERT_TAIL(&fc_conn->pool_queue, req, pool_link);
|
|
}
|
|
return 0;
|
|
error:
|
|
nvmf_fc_free_conn_reqpool(fc_conn);
|
|
return -1;
|
|
}
|
|
|
|
static inline struct spdk_nvmf_fc_request *
|
|
nvmf_fc_conn_alloc_fc_request(struct spdk_nvmf_fc_conn *fc_conn)
|
|
{
|
|
struct spdk_nvmf_fc_request *fc_req;
|
|
struct spdk_nvmf_fc_pooled_request *pooled_req;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = fc_conn->hwqp;
|
|
|
|
pooled_req = STAILQ_FIRST(&fc_conn->pool_queue);
|
|
if (!pooled_req) {
|
|
SPDK_ERRLOG("Alloc request buffer failed\n");
|
|
return NULL;
|
|
}
|
|
STAILQ_REMOVE_HEAD(&fc_conn->pool_queue, pool_link);
|
|
fc_conn->pool_free_elems -= 1;
|
|
|
|
fc_req = (struct spdk_nvmf_fc_request *)pooled_req;
|
|
memset(fc_req, 0, sizeof(struct spdk_nvmf_fc_request));
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_INIT);
|
|
|
|
TAILQ_INSERT_TAIL(&hwqp->in_use_reqs, fc_req, link);
|
|
TAILQ_INSERT_TAIL(&fc_conn->in_use_reqs, fc_req, conn_link);
|
|
TAILQ_INIT(&fc_req->abort_cbs);
|
|
return fc_req;
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_conn_free_fc_request(struct spdk_nvmf_fc_conn *fc_conn, struct spdk_nvmf_fc_request *fc_req)
|
|
{
|
|
if (fc_req->state != SPDK_NVMF_FC_REQ_SUCCESS) {
|
|
/* Log an error for debug purpose. */
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_FAILED);
|
|
}
|
|
|
|
/* set the magic to mark req as no longer valid. */
|
|
fc_req->magic = 0xDEADBEEF;
|
|
|
|
TAILQ_REMOVE(&fc_conn->hwqp->in_use_reqs, fc_req, link);
|
|
TAILQ_REMOVE(&fc_conn->in_use_reqs, fc_req, conn_link);
|
|
|
|
STAILQ_INSERT_HEAD(&fc_conn->pool_queue, (struct spdk_nvmf_fc_pooled_request *)fc_req, pool_link);
|
|
fc_conn->pool_free_elems += 1;
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_request_remove_from_pending(struct spdk_nvmf_fc_request *fc_req)
|
|
{
|
|
STAILQ_REMOVE(&fc_req->hwqp->fgroup->group.pending_buf_queue, &fc_req->req,
|
|
spdk_nvmf_request, buf_link);
|
|
}
|
|
|
|
int
|
|
nvmf_fc_init_hwqp(struct spdk_nvmf_fc_port *fc_port, struct spdk_nvmf_fc_hwqp *hwqp)
|
|
{
|
|
char name[64];
|
|
|
|
hwqp->fc_port = fc_port;
|
|
|
|
/* clear counters */
|
|
memset(&hwqp->counters, 0, sizeof(struct spdk_nvmf_fc_errors));
|
|
|
|
TAILQ_INIT(&hwqp->in_use_reqs);
|
|
TAILQ_INIT(&hwqp->sync_cbs);
|
|
TAILQ_INIT(&hwqp->ls_pending_queue);
|
|
|
|
snprintf(name, sizeof(name), "nvmf_fc_conn_hash:%d-%d", fc_port->port_hdl, hwqp->hwqp_id);
|
|
hwqp->connection_list_hash = nvmf_fc_create_hash_table(name, HWQP_CONN_TABLE_SIZE,
|
|
sizeof(uint64_t));
|
|
if (!hwqp->connection_list_hash) {
|
|
SPDK_ERRLOG("Failed to create connection hash table.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
snprintf(name, sizeof(name), "nvmf_fc_rpi_hash:%d-%d", fc_port->port_hdl, hwqp->hwqp_id);
|
|
hwqp->rport_list_hash = nvmf_fc_create_hash_table(name, HWQP_RPI_TABLE_SIZE, sizeof(uint16_t));
|
|
if (!hwqp->rport_list_hash) {
|
|
SPDK_ERRLOG("Failed to create rpi hash table.\n");
|
|
rte_hash_free(hwqp->connection_list_hash);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Init low level driver queues */
|
|
nvmf_fc_init_q(hwqp);
|
|
return 0;
|
|
}
|
|
|
|
static struct spdk_nvmf_fc_poll_group *
|
|
nvmf_fc_get_idlest_poll_group(void)
|
|
{
|
|
uint32_t max_count = UINT32_MAX;
|
|
struct spdk_nvmf_fc_poll_group *fgroup;
|
|
struct spdk_nvmf_fc_poll_group *ret_fgroup = NULL;
|
|
|
|
/* find poll group with least number of hwqp's assigned to it */
|
|
TAILQ_FOREACH(fgroup, &g_nvmf_fgroups, link) {
|
|
if (fgroup->hwqp_count < max_count) {
|
|
ret_fgroup = fgroup;
|
|
max_count = fgroup->hwqp_count;
|
|
}
|
|
}
|
|
|
|
return ret_fgroup;
|
|
}
|
|
|
|
void
|
|
nvmf_fc_poll_group_add_hwqp(struct spdk_nvmf_fc_hwqp *hwqp)
|
|
{
|
|
struct spdk_nvmf_fc_poll_group *fgroup = NULL;
|
|
|
|
assert(hwqp);
|
|
if (hwqp == NULL) {
|
|
SPDK_ERRLOG("Error: hwqp is NULL\n");
|
|
return;
|
|
}
|
|
|
|
assert(g_nvmf_fgroup_count);
|
|
|
|
fgroup = nvmf_fc_get_idlest_poll_group();
|
|
if (!fgroup) {
|
|
SPDK_ERRLOG("Could not assign poll group for hwqp (%d)\n", hwqp->hwqp_id);
|
|
return;
|
|
}
|
|
|
|
hwqp->thread = fgroup->group.group->thread;
|
|
hwqp->fgroup = fgroup;
|
|
fgroup->hwqp_count++;
|
|
nvmf_fc_poller_api_func(hwqp, SPDK_NVMF_FC_POLLER_API_ADD_HWQP, NULL);
|
|
}
|
|
|
|
void
|
|
nvmf_fc_poll_group_remove_hwqp(struct spdk_nvmf_fc_hwqp *hwqp)
|
|
{
|
|
assert(hwqp);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc,
|
|
"Remove hwqp from poller: for port: %d, hwqp: %d\n",
|
|
hwqp->fc_port->port_hdl, hwqp->hwqp_id);
|
|
|
|
if (!hwqp->fgroup) {
|
|
SPDK_ERRLOG("HWQP (%d) not assigned to poll group\n", hwqp->hwqp_id);
|
|
} else {
|
|
hwqp->fgroup->hwqp_count--;
|
|
nvmf_fc_poller_api_func(hwqp, SPDK_NVMF_FC_POLLER_API_REMOVE_HWQP, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Note: This needs to be used only on main poller.
|
|
*/
|
|
static uint64_t
|
|
nvmf_fc_get_abts_unique_id(void)
|
|
{
|
|
static uint32_t u_id = 0;
|
|
|
|
return (uint64_t)(++u_id);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_queue_synced_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
struct spdk_nvmf_fc_abts_ctx *ctx = cb_data;
|
|
struct spdk_nvmf_fc_poller_api_abts_recvd_args *args, *poller_arg;
|
|
|
|
ctx->hwqps_responded++;
|
|
|
|
if (ctx->hwqps_responded < ctx->num_hwqps) {
|
|
/* Wait for all pollers to complete. */
|
|
return;
|
|
}
|
|
|
|
/* Free the queue sync poller args. */
|
|
free(ctx->sync_poller_args);
|
|
|
|
/* Mark as queue synced */
|
|
ctx->queue_synced = true;
|
|
|
|
/* Reset the ctx values */
|
|
ctx->hwqps_responded = 0;
|
|
ctx->handled = false;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc,
|
|
"QueueSync(0x%lx) completed for nport: %d, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
ctx->u_id, ctx->nport->nport_hdl, ctx->rpi, ctx->oxid, ctx->rxid);
|
|
|
|
/* Resend ABTS to pollers */
|
|
args = ctx->abts_poller_args;
|
|
for (int i = 0; i < ctx->num_hwqps; i++) {
|
|
poller_arg = args + i;
|
|
nvmf_fc_poller_api_func(poller_arg->hwqp,
|
|
SPDK_NVMF_FC_POLLER_API_ABTS_RECEIVED,
|
|
poller_arg);
|
|
}
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_handle_abts_notfound(struct spdk_nvmf_fc_abts_ctx *ctx)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_queue_sync_args *args, *poller_arg;
|
|
struct spdk_nvmf_fc_poller_api_abts_recvd_args *abts_args, *abts_poller_arg;
|
|
|
|
/* check if FC driver supports queue sync */
|
|
if (!nvmf_fc_q_sync_available()) {
|
|
return -EPERM;
|
|
}
|
|
|
|
assert(ctx);
|
|
if (!ctx) {
|
|
SPDK_ERRLOG("NULL ctx pointer");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Reset the ctx values */
|
|
ctx->hwqps_responded = 0;
|
|
|
|
args = calloc(ctx->num_hwqps,
|
|
sizeof(struct spdk_nvmf_fc_poller_api_queue_sync_args));
|
|
if (!args) {
|
|
SPDK_ERRLOG("QueueSync(0x%lx) failed for nport: %d, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
ctx->u_id, ctx->nport->nport_hdl, ctx->rpi, ctx->oxid, ctx->rxid);
|
|
return -ENOMEM;
|
|
}
|
|
ctx->sync_poller_args = args;
|
|
|
|
abts_args = ctx->abts_poller_args;
|
|
for (int i = 0; i < ctx->num_hwqps; i++) {
|
|
abts_poller_arg = abts_args + i;
|
|
poller_arg = args + i;
|
|
poller_arg->u_id = ctx->u_id;
|
|
poller_arg->hwqp = abts_poller_arg->hwqp;
|
|
poller_arg->cb_info.cb_func = nvmf_fc_queue_synced_cb;
|
|
poller_arg->cb_info.cb_data = ctx;
|
|
poller_arg->cb_info.cb_thread = spdk_get_thread();
|
|
|
|
/* Send a Queue sync message to interested pollers */
|
|
nvmf_fc_poller_api_func(poller_arg->hwqp,
|
|
SPDK_NVMF_FC_POLLER_API_QUEUE_SYNC,
|
|
poller_arg);
|
|
}
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc,
|
|
"QueueSync(0x%lx) Sent for nport: %d, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
ctx->u_id, ctx->nport->nport_hdl, ctx->rpi, ctx->oxid, ctx->rxid);
|
|
|
|
/* Post Marker to queue to track aborted request */
|
|
nvmf_fc_issue_q_sync(ctx->ls_hwqp, ctx->u_id, ctx->fcp_rq_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_abts_handled_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
struct spdk_nvmf_fc_abts_ctx *ctx = cb_data;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
|
|
if (ret != SPDK_NVMF_FC_POLLER_API_OXID_NOT_FOUND) {
|
|
ctx->handled = true;
|
|
}
|
|
|
|
ctx->hwqps_responded++;
|
|
|
|
if (ctx->hwqps_responded < ctx->num_hwqps) {
|
|
/* Wait for all pollers to complete. */
|
|
return;
|
|
}
|
|
|
|
nport = nvmf_fc_nport_find(ctx->port_hdl, ctx->nport_hdl);
|
|
|
|
if (ctx->nport != nport) {
|
|
/* Nport can be deleted while this abort is being
|
|
* processed by the pollers.
|
|
*/
|
|
SPDK_NOTICELOG("nport_%d deleted while processing ABTS frame, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
ctx->nport_hdl, ctx->rpi, ctx->oxid, ctx->rxid);
|
|
} else {
|
|
if (!ctx->handled) {
|
|
/* Try syncing the queues and try one more time */
|
|
if (!ctx->queue_synced && (nvmf_fc_handle_abts_notfound(ctx) == 0)) {
|
|
SPDK_DEBUGLOG(nvmf_fc,
|
|
"QueueSync(0x%lx) for nport: %d, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
ctx->u_id, ctx->nport->nport_hdl, ctx->rpi, ctx->oxid, ctx->rxid);
|
|
return;
|
|
} else {
|
|
/* Send Reject */
|
|
nvmf_fc_xmt_bls_rsp(&ctx->nport->fc_port->ls_queue,
|
|
ctx->oxid, ctx->rxid, ctx->rpi, true,
|
|
FCNVME_BLS_REJECT_EXP_INVALID_OXID, NULL, NULL);
|
|
}
|
|
} else {
|
|
/* Send Accept */
|
|
nvmf_fc_xmt_bls_rsp(&ctx->nport->fc_port->ls_queue,
|
|
ctx->oxid, ctx->rxid, ctx->rpi, false,
|
|
0, NULL, NULL);
|
|
}
|
|
}
|
|
SPDK_NOTICELOG("BLS_%s sent for ABTS frame nport: %d, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
(ctx->handled) ? "ACC" : "REJ", ctx->nport->nport_hdl, ctx->rpi, ctx->oxid, ctx->rxid);
|
|
|
|
free(ctx->abts_poller_args);
|
|
free(ctx);
|
|
}
|
|
|
|
void
|
|
nvmf_fc_handle_abts_frame(struct spdk_nvmf_fc_nport *nport, uint16_t rpi,
|
|
uint16_t oxid, uint16_t rxid)
|
|
{
|
|
struct spdk_nvmf_fc_abts_ctx *ctx = NULL;
|
|
struct spdk_nvmf_fc_poller_api_abts_recvd_args *args = NULL, *poller_arg;
|
|
struct spdk_nvmf_fc_association *assoc = NULL;
|
|
struct spdk_nvmf_fc_conn *conn = NULL;
|
|
uint32_t hwqp_cnt = 0;
|
|
bool skip_hwqp_cnt;
|
|
struct spdk_nvmf_fc_hwqp **hwqps = NULL;
|
|
uint32_t i;
|
|
|
|
SPDK_NOTICELOG("Handle ABTS frame for nport: %d, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
nport->nport_hdl, rpi, oxid, rxid);
|
|
|
|
/* Allocate memory to track hwqp's with at least 1 active connection. */
|
|
hwqps = calloc(nport->fc_port->num_io_queues, sizeof(struct spdk_nvmf_fc_hwqp *));
|
|
if (hwqps == NULL) {
|
|
SPDK_ERRLOG("Unable to allocate temp. hwqp array for abts processing!\n");
|
|
goto bls_rej;
|
|
}
|
|
|
|
TAILQ_FOREACH(assoc, &nport->fc_associations, link) {
|
|
TAILQ_FOREACH(conn, &assoc->fc_conns, assoc_link) {
|
|
if (conn->rpi != rpi) {
|
|
continue;
|
|
}
|
|
|
|
skip_hwqp_cnt = false;
|
|
for (i = 0; i < hwqp_cnt; i++) {
|
|
if (hwqps[i] == conn->hwqp) {
|
|
/* Skip. This is already present */
|
|
skip_hwqp_cnt = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!skip_hwqp_cnt) {
|
|
assert(hwqp_cnt < nport->fc_port->num_io_queues);
|
|
hwqps[hwqp_cnt] = conn->hwqp;
|
|
hwqp_cnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!hwqp_cnt) {
|
|
goto bls_rej;
|
|
}
|
|
|
|
args = calloc(hwqp_cnt,
|
|
sizeof(struct spdk_nvmf_fc_poller_api_abts_recvd_args));
|
|
if (!args) {
|
|
goto bls_rej;
|
|
}
|
|
|
|
ctx = calloc(1, sizeof(struct spdk_nvmf_fc_abts_ctx));
|
|
if (!ctx) {
|
|
goto bls_rej;
|
|
}
|
|
ctx->rpi = rpi;
|
|
ctx->oxid = oxid;
|
|
ctx->rxid = rxid;
|
|
ctx->nport = nport;
|
|
ctx->nport_hdl = nport->nport_hdl;
|
|
ctx->port_hdl = nport->fc_port->port_hdl;
|
|
ctx->num_hwqps = hwqp_cnt;
|
|
ctx->ls_hwqp = &nport->fc_port->ls_queue;
|
|
ctx->fcp_rq_id = nport->fc_port->fcp_rq_id;
|
|
ctx->abts_poller_args = args;
|
|
|
|
/* Get a unique context for this ABTS */
|
|
ctx->u_id = nvmf_fc_get_abts_unique_id();
|
|
|
|
for (i = 0; i < hwqp_cnt; i++) {
|
|
poller_arg = args + i;
|
|
poller_arg->hwqp = hwqps[i];
|
|
poller_arg->cb_info.cb_func = nvmf_fc_abts_handled_cb;
|
|
poller_arg->cb_info.cb_data = ctx;
|
|
poller_arg->cb_info.cb_thread = spdk_get_thread();
|
|
poller_arg->ctx = ctx;
|
|
|
|
nvmf_fc_poller_api_func(poller_arg->hwqp,
|
|
SPDK_NVMF_FC_POLLER_API_ABTS_RECEIVED,
|
|
poller_arg);
|
|
}
|
|
|
|
free(hwqps);
|
|
|
|
return;
|
|
bls_rej:
|
|
free(args);
|
|
free(hwqps);
|
|
|
|
/* Send Reject */
|
|
nvmf_fc_xmt_bls_rsp(&nport->fc_port->ls_queue, oxid, rxid, rpi,
|
|
true, FCNVME_BLS_REJECT_EXP_NOINFO, NULL, NULL);
|
|
SPDK_NOTICELOG("BLS_RJT for ABTS frame for nport: %d, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
nport->nport_hdl, rpi, oxid, rxid);
|
|
return;
|
|
}
|
|
|
|
/*** Accessor functions for the FC structures - BEGIN */
|
|
/*
|
|
* Returns true if the port is in offline state.
|
|
*/
|
|
bool
|
|
nvmf_fc_port_is_offline(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
if (fc_port && (fc_port->hw_port_status == SPDK_FC_PORT_OFFLINE)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Returns true if the port is in online state.
|
|
*/
|
|
bool
|
|
nvmf_fc_port_is_online(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
if (fc_port && (fc_port->hw_port_status == SPDK_FC_PORT_ONLINE)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int
|
|
nvmf_fc_port_set_online(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
if (fc_port && (fc_port->hw_port_status != SPDK_FC_PORT_ONLINE)) {
|
|
fc_port->hw_port_status = SPDK_FC_PORT_ONLINE;
|
|
return 0;
|
|
}
|
|
|
|
return -EPERM;
|
|
}
|
|
|
|
int
|
|
nvmf_fc_port_set_offline(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
if (fc_port && (fc_port->hw_port_status != SPDK_FC_PORT_OFFLINE)) {
|
|
fc_port->hw_port_status = SPDK_FC_PORT_OFFLINE;
|
|
return 0;
|
|
}
|
|
|
|
return -EPERM;
|
|
}
|
|
|
|
int
|
|
nvmf_fc_hwqp_set_online(struct spdk_nvmf_fc_hwqp *hwqp)
|
|
{
|
|
if (hwqp && (hwqp->state != SPDK_FC_HWQP_ONLINE)) {
|
|
hwqp->state = SPDK_FC_HWQP_ONLINE;
|
|
/* reset some queue counters */
|
|
hwqp->num_conns = 0;
|
|
return nvmf_fc_set_q_online_state(hwqp, true);
|
|
}
|
|
|
|
return -EPERM;
|
|
}
|
|
|
|
int
|
|
nvmf_fc_hwqp_set_offline(struct spdk_nvmf_fc_hwqp *hwqp)
|
|
{
|
|
if (hwqp && (hwqp->state != SPDK_FC_HWQP_OFFLINE)) {
|
|
hwqp->state = SPDK_FC_HWQP_OFFLINE;
|
|
return nvmf_fc_set_q_online_state(hwqp, false);
|
|
}
|
|
|
|
return -EPERM;
|
|
}
|
|
|
|
void
|
|
nvmf_fc_port_add(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
TAILQ_INSERT_TAIL(&g_spdk_nvmf_fc_port_list, fc_port, link);
|
|
}
|
|
|
|
struct spdk_nvmf_fc_port *
|
|
nvmf_fc_port_lookup(uint8_t port_hdl)
|
|
{
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
|
|
TAILQ_FOREACH(fc_port, &g_spdk_nvmf_fc_port_list, link) {
|
|
if (fc_port->port_hdl == port_hdl) {
|
|
return fc_port;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_port_cleanup(void)
|
|
{
|
|
struct spdk_nvmf_fc_port *fc_port, *tmp;
|
|
|
|
TAILQ_FOREACH_SAFE(fc_port, &g_spdk_nvmf_fc_port_list, link, tmp) {
|
|
TAILQ_REMOVE(&g_spdk_nvmf_fc_port_list, fc_port, link);
|
|
free(fc_port);
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
nvmf_fc_get_prli_service_params(void)
|
|
{
|
|
return (SPDK_NVMF_FC_DISCOVERY_SERVICE | SPDK_NVMF_FC_TARGET_FUNCTION);
|
|
}
|
|
|
|
int
|
|
nvmf_fc_port_add_nport(struct spdk_nvmf_fc_port *fc_port,
|
|
struct spdk_nvmf_fc_nport *nport)
|
|
{
|
|
if (fc_port) {
|
|
TAILQ_INSERT_TAIL(&fc_port->nport_list, nport, link);
|
|
fc_port->num_nports++;
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int
|
|
nvmf_fc_port_remove_nport(struct spdk_nvmf_fc_port *fc_port,
|
|
struct spdk_nvmf_fc_nport *nport)
|
|
{
|
|
if (fc_port && nport) {
|
|
TAILQ_REMOVE(&fc_port->nport_list, nport, link);
|
|
fc_port->num_nports--;
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct spdk_nvmf_fc_nport *
|
|
nvmf_fc_nport_hdl_lookup(struct spdk_nvmf_fc_port *fc_port, uint16_t nport_hdl)
|
|
{
|
|
struct spdk_nvmf_fc_nport *fc_nport = NULL;
|
|
|
|
TAILQ_FOREACH(fc_nport, &fc_port->nport_list, link) {
|
|
if (fc_nport->nport_hdl == nport_hdl) {
|
|
return fc_nport;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct spdk_nvmf_fc_nport *
|
|
nvmf_fc_nport_find(uint8_t port_hdl, uint16_t nport_hdl)
|
|
{
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
|
|
fc_port = nvmf_fc_port_lookup(port_hdl);
|
|
if (fc_port) {
|
|
return nvmf_fc_nport_hdl_lookup(fc_port, nport_hdl);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static inline int
|
|
nvmf_fc_hwqp_find_nport_and_rport(struct spdk_nvmf_fc_hwqp *hwqp,
|
|
uint32_t d_id, struct spdk_nvmf_fc_nport **nport,
|
|
uint32_t s_id, struct spdk_nvmf_fc_remote_port_info **rport)
|
|
{
|
|
struct spdk_nvmf_fc_nport *n_port;
|
|
struct spdk_nvmf_fc_remote_port_info *r_port;
|
|
|
|
assert(hwqp);
|
|
if (hwqp == NULL) {
|
|
SPDK_ERRLOG("Error: hwqp is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
assert(nport);
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Error: nport is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
assert(rport);
|
|
if (rport == NULL) {
|
|
SPDK_ERRLOG("Error: rport is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
TAILQ_FOREACH(n_port, &hwqp->fc_port->nport_list, link) {
|
|
if (n_port->d_id == d_id) {
|
|
TAILQ_FOREACH(r_port, &n_port->rem_port_list, link) {
|
|
if (r_port->s_id == s_id) {
|
|
*nport = n_port;
|
|
*rport = r_port;
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Returns true if the Nport is empty of all rem_ports */
|
|
bool
|
|
nvmf_fc_nport_has_no_rport(struct spdk_nvmf_fc_nport *nport)
|
|
{
|
|
if (nport && TAILQ_EMPTY(&nport->rem_port_list)) {
|
|
assert(nport->rport_count == 0);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int
|
|
nvmf_fc_nport_set_state(struct spdk_nvmf_fc_nport *nport,
|
|
enum spdk_nvmf_fc_object_state state)
|
|
{
|
|
if (nport) {
|
|
nport->nport_state = state;
|
|
return 0;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
bool
|
|
nvmf_fc_nport_add_rem_port(struct spdk_nvmf_fc_nport *nport,
|
|
struct spdk_nvmf_fc_remote_port_info *rem_port)
|
|
{
|
|
if (nport && rem_port) {
|
|
TAILQ_INSERT_TAIL(&nport->rem_port_list, rem_port, link);
|
|
nport->rport_count++;
|
|
return 0;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
bool
|
|
nvmf_fc_nport_remove_rem_port(struct spdk_nvmf_fc_nport *nport,
|
|
struct spdk_nvmf_fc_remote_port_info *rem_port)
|
|
{
|
|
if (nport && rem_port) {
|
|
TAILQ_REMOVE(&nport->rem_port_list, rem_port, link);
|
|
nport->rport_count--;
|
|
return 0;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
int
|
|
nvmf_fc_rport_set_state(struct spdk_nvmf_fc_remote_port_info *rport,
|
|
enum spdk_nvmf_fc_object_state state)
|
|
{
|
|
if (rport) {
|
|
rport->rport_state = state;
|
|
return 0;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
int
|
|
nvmf_fc_assoc_set_state(struct spdk_nvmf_fc_association *assoc,
|
|
enum spdk_nvmf_fc_object_state state)
|
|
{
|
|
if (assoc) {
|
|
assoc->assoc_state = state;
|
|
return 0;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static struct spdk_nvmf_fc_association *
|
|
nvmf_ctrlr_get_fc_assoc(struct spdk_nvmf_ctrlr *ctrlr)
|
|
{
|
|
struct spdk_nvmf_qpair *qpair = ctrlr->admin_qpair;
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
if (!qpair) {
|
|
SPDK_ERRLOG("Controller %d has no associations\n", ctrlr->cntlid);
|
|
return NULL;
|
|
}
|
|
|
|
fc_conn = SPDK_CONTAINEROF(qpair, struct spdk_nvmf_fc_conn, qpair);
|
|
|
|
return fc_conn->fc_assoc;
|
|
}
|
|
|
|
bool
|
|
nvmf_ctrlr_is_on_nport(uint8_t port_hdl, uint16_t nport_hdl,
|
|
struct spdk_nvmf_ctrlr *ctrlr)
|
|
{
|
|
struct spdk_nvmf_fc_nport *fc_nport = NULL;
|
|
struct spdk_nvmf_fc_association *assoc = NULL;
|
|
|
|
if (!ctrlr) {
|
|
return false;
|
|
}
|
|
|
|
fc_nport = nvmf_fc_nport_find(port_hdl, nport_hdl);
|
|
if (!fc_nport) {
|
|
return false;
|
|
}
|
|
|
|
assoc = nvmf_ctrlr_get_fc_assoc(ctrlr);
|
|
if (assoc && assoc->tgtport == fc_nport) {
|
|
SPDK_DEBUGLOG(nvmf_fc,
|
|
"Controller: %d corresponding to association: %p(%lu:%d) is on port: %d nport: %d\n",
|
|
ctrlr->cntlid, assoc, assoc->assoc_id, assoc->assoc_state, port_hdl,
|
|
nport_hdl);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_req_bdev_abort(void *arg1)
|
|
{
|
|
struct spdk_nvmf_fc_request *fc_req = arg1;
|
|
struct spdk_nvmf_ctrlr *ctrlr = fc_req->req.qpair->ctrlr;
|
|
int i;
|
|
|
|
/* Initial release - we don't have to abort Admin Queue or
|
|
* Fabric commands. The AQ commands supported at this time are
|
|
* Get-Log-Page,
|
|
* Identify
|
|
* Set Features
|
|
* Get Features
|
|
* AER -> Special case and handled differently.
|
|
* Every one of the above Admin commands (except AER) run
|
|
* to completion and so an Abort of such commands doesn't
|
|
* make sense.
|
|
*/
|
|
/* The Fabric commands supported are
|
|
* Property Set
|
|
* Property Get
|
|
* Connect -> Special case (async. handling). Not sure how to
|
|
* handle at this point. Let it run to completion.
|
|
*/
|
|
for (i = 0; i < NVMF_MAX_ASYNC_EVENTS; i++) {
|
|
if (ctrlr->aer_req[i] == &fc_req->req) {
|
|
SPDK_NOTICELOG("Abort AER request\n");
|
|
nvmf_qpair_free_aer(fc_req->req.qpair);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nvmf_fc_request_abort_complete(void *arg1)
|
|
{
|
|
struct spdk_nvmf_fc_request *fc_req =
|
|
(struct spdk_nvmf_fc_request *)arg1;
|
|
struct spdk_nvmf_fc_caller_ctx *ctx = NULL, *tmp = NULL;
|
|
TAILQ_HEAD(, spdk_nvmf_fc_caller_ctx) abort_cbs;
|
|
|
|
/* Make a copy of the cb list from fc_req */
|
|
TAILQ_INIT(&abort_cbs);
|
|
TAILQ_SWAP(&abort_cbs, &fc_req->abort_cbs, spdk_nvmf_fc_caller_ctx, link);
|
|
|
|
SPDK_NOTICELOG("FC Request(%p) in state :%s aborted\n", fc_req,
|
|
fc_req_state_strs[fc_req->state]);
|
|
|
|
_nvmf_fc_request_free(fc_req);
|
|
|
|
/* Request abort completed. Notify all the callbacks */
|
|
TAILQ_FOREACH_SAFE(ctx, &abort_cbs, link, tmp) {
|
|
/* Notify */
|
|
ctx->cb(fc_req->hwqp, 0, ctx->cb_args);
|
|
/* Remove */
|
|
TAILQ_REMOVE(&abort_cbs, ctx, link);
|
|
/* free */
|
|
free(ctx);
|
|
}
|
|
}
|
|
|
|
void
|
|
nvmf_fc_request_abort(struct spdk_nvmf_fc_request *fc_req, bool send_abts,
|
|
spdk_nvmf_fc_caller_cb cb, void *cb_args)
|
|
{
|
|
struct spdk_nvmf_fc_caller_ctx *ctx = NULL;
|
|
bool kill_req = false;
|
|
|
|
/* Add the cb to list */
|
|
if (cb) {
|
|
ctx = calloc(1, sizeof(struct spdk_nvmf_fc_caller_ctx));
|
|
if (!ctx) {
|
|
SPDK_ERRLOG("ctx alloc failed.\n");
|
|
return;
|
|
}
|
|
ctx->cb = cb;
|
|
ctx->cb_args = cb_args;
|
|
|
|
TAILQ_INSERT_TAIL(&fc_req->abort_cbs, ctx, link);
|
|
}
|
|
|
|
if (!fc_req->is_aborted) {
|
|
/* Increment aborted command counter */
|
|
fc_req->hwqp->counters.num_aborted++;
|
|
}
|
|
|
|
/* If port is dead, skip abort wqe */
|
|
kill_req = nvmf_fc_is_port_dead(fc_req->hwqp);
|
|
if (kill_req && nvmf_fc_req_in_xfer(fc_req)) {
|
|
fc_req->is_aborted = true;
|
|
goto complete;
|
|
}
|
|
|
|
/* Check if the request is already marked for deletion */
|
|
if (fc_req->is_aborted) {
|
|
return;
|
|
}
|
|
|
|
/* Mark request as aborted */
|
|
fc_req->is_aborted = true;
|
|
|
|
/* If xchg is allocated, then save if we need to send abts or not. */
|
|
if (fc_req->xchg) {
|
|
fc_req->xchg->send_abts = send_abts;
|
|
fc_req->xchg->aborted = true;
|
|
}
|
|
|
|
switch (fc_req->state) {
|
|
case SPDK_NVMF_FC_REQ_BDEV_ABORTED:
|
|
/* Aborted by backend */
|
|
goto complete;
|
|
|
|
case SPDK_NVMF_FC_REQ_READ_BDEV:
|
|
case SPDK_NVMF_FC_REQ_WRITE_BDEV:
|
|
case SPDK_NVMF_FC_REQ_NONE_BDEV:
|
|
/* Notify bdev */
|
|
spdk_thread_send_msg(fc_req->hwqp->thread,
|
|
nvmf_fc_req_bdev_abort, (void *)fc_req);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_REQ_READ_XFER:
|
|
case SPDK_NVMF_FC_REQ_READ_RSP:
|
|
case SPDK_NVMF_FC_REQ_WRITE_XFER:
|
|
case SPDK_NVMF_FC_REQ_WRITE_RSP:
|
|
case SPDK_NVMF_FC_REQ_NONE_RSP:
|
|
/* Notify HBA to abort this exchange */
|
|
nvmf_fc_issue_abort(fc_req->hwqp, fc_req->xchg, NULL, NULL);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_REQ_PENDING:
|
|
/* Remove from pending */
|
|
nvmf_fc_request_remove_from_pending(fc_req);
|
|
goto complete;
|
|
default:
|
|
SPDK_ERRLOG("Request in invalid state.\n");
|
|
goto complete;
|
|
}
|
|
|
|
return;
|
|
complete:
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_ABORTED);
|
|
nvmf_fc_poller_api_func(fc_req->hwqp, SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE,
|
|
(void *)fc_req);
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_request_alloc_buffers(struct spdk_nvmf_fc_request *fc_req)
|
|
{
|
|
uint32_t length = fc_req->req.length;
|
|
struct spdk_nvmf_fc_poll_group *fgroup = fc_req->hwqp->fgroup;
|
|
struct spdk_nvmf_transport_poll_group *group = &fgroup->group;
|
|
struct spdk_nvmf_transport *transport = group->transport;
|
|
|
|
if (spdk_nvmf_request_get_buffers(&fc_req->req, group, transport, length)) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_request_execute(struct spdk_nvmf_fc_request *fc_req)
|
|
{
|
|
/* Allocate an XCHG if we dont use send frame for this command. */
|
|
if (!nvmf_fc_use_send_frame(&fc_req->req)) {
|
|
fc_req->xchg = nvmf_fc_get_xri(fc_req->hwqp);
|
|
if (!fc_req->xchg) {
|
|
fc_req->hwqp->counters.no_xchg++;
|
|
return -EAGAIN;
|
|
}
|
|
}
|
|
|
|
if (fc_req->req.length) {
|
|
if (nvmf_fc_request_alloc_buffers(fc_req) < 0) {
|
|
fc_req->hwqp->counters.buf_alloc_err++;
|
|
if (fc_req->xchg) {
|
|
nvmf_fc_put_xchg(fc_req->hwqp, fc_req->xchg);
|
|
fc_req->xchg = NULL;
|
|
}
|
|
return -EAGAIN;
|
|
}
|
|
fc_req->req.data = fc_req->req.iov[0].iov_base;
|
|
}
|
|
|
|
if (fc_req->req.xfer == SPDK_NVME_DATA_HOST_TO_CONTROLLER) {
|
|
SPDK_DEBUGLOG(nvmf_fc, "WRITE CMD.\n");
|
|
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_WRITE_XFER);
|
|
|
|
if (nvmf_fc_recv_data(fc_req)) {
|
|
/* Dropped return success to caller */
|
|
fc_req->hwqp->counters.unexpected_err++;
|
|
_nvmf_fc_request_free(fc_req);
|
|
}
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc, "READ/NONE CMD\n");
|
|
|
|
if (fc_req->req.xfer == SPDK_NVME_DATA_CONTROLLER_TO_HOST) {
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_READ_BDEV);
|
|
} else {
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_NONE_BDEV);
|
|
}
|
|
spdk_nvmf_request_exec(&fc_req->req);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_hwqp_handle_request(struct spdk_nvmf_fc_hwqp *hwqp, struct spdk_nvmf_fc_frame_hdr *frame,
|
|
struct spdk_nvmf_fc_buffer_desc *buffer, uint32_t plen)
|
|
{
|
|
uint16_t cmnd_len;
|
|
uint64_t rqst_conn_id;
|
|
struct spdk_nvmf_fc_request *fc_req = NULL;
|
|
struct spdk_nvmf_fc_cmnd_iu *cmd_iu = NULL;
|
|
struct spdk_nvmf_fc_conn *fc_conn = NULL;
|
|
enum spdk_nvme_data_transfer xfer;
|
|
uint32_t s_id, d_id;
|
|
|
|
s_id = (uint32_t)frame->s_id;
|
|
d_id = (uint32_t)frame->d_id;
|
|
s_id = from_be32(&s_id) >> 8;
|
|
d_id = from_be32(&d_id) >> 8;
|
|
|
|
cmd_iu = buffer->virt;
|
|
cmnd_len = cmd_iu->cmnd_iu_len;
|
|
cmnd_len = from_be16(&cmnd_len);
|
|
|
|
/* check for a valid cmnd_iu format */
|
|
if ((cmd_iu->fc_id != FCNVME_CMND_IU_FC_ID) ||
|
|
(cmd_iu->scsi_id != FCNVME_CMND_IU_SCSI_ID) ||
|
|
(cmnd_len != sizeof(struct spdk_nvmf_fc_cmnd_iu) / 4)) {
|
|
SPDK_ERRLOG("IU CMD error\n");
|
|
hwqp->counters.nvme_cmd_iu_err++;
|
|
return -ENXIO;
|
|
}
|
|
|
|
xfer = spdk_nvme_opc_get_data_transfer(cmd_iu->flags);
|
|
if (xfer == SPDK_NVME_DATA_BIDIRECTIONAL) {
|
|
SPDK_ERRLOG("IU CMD xfer error\n");
|
|
hwqp->counters.nvme_cmd_xfer_err++;
|
|
return -EPERM;
|
|
}
|
|
|
|
rqst_conn_id = from_be64(&cmd_iu->conn_id);
|
|
|
|
if (rte_hash_lookup_data(hwqp->connection_list_hash,
|
|
(void *)&rqst_conn_id, (void **)&fc_conn) < 0) {
|
|
SPDK_ERRLOG("IU CMD conn(%ld) invalid\n", rqst_conn_id);
|
|
hwqp->counters.invalid_conn_err++;
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Validate s_id and d_id */
|
|
if (s_id != fc_conn->s_id) {
|
|
hwqp->counters.rport_invalid++;
|
|
SPDK_ERRLOG("Frame s_id invalid for connection %ld\n", rqst_conn_id);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (d_id != fc_conn->d_id) {
|
|
hwqp->counters.nport_invalid++;
|
|
SPDK_ERRLOG("Frame d_id invalid for connection %ld\n", rqst_conn_id);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* If association/connection is being deleted - return */
|
|
if (fc_conn->fc_assoc->assoc_state != SPDK_NVMF_FC_OBJECT_CREATED) {
|
|
SPDK_ERRLOG("Association state not valid\n");
|
|
return -EACCES;
|
|
}
|
|
|
|
if (fc_conn->qpair.state == SPDK_NVMF_QPAIR_ERROR) {
|
|
return -EACCES;
|
|
}
|
|
|
|
/* Make sure xfer len is according to mdts */
|
|
if (from_be32(&cmd_iu->data_len) >
|
|
hwqp->fgroup->group.transport->opts.max_io_size) {
|
|
SPDK_ERRLOG("IO length requested is greater than MDTS\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* allocate a request buffer */
|
|
fc_req = nvmf_fc_conn_alloc_fc_request(fc_conn);
|
|
if (fc_req == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
fc_req->req.length = from_be32(&cmd_iu->data_len);
|
|
fc_req->req.qpair = &fc_conn->qpair;
|
|
memcpy(&fc_req->cmd, &cmd_iu->cmd, sizeof(union nvmf_h2c_msg));
|
|
fc_req->req.cmd = (union nvmf_h2c_msg *)&fc_req->cmd;
|
|
fc_req->req.rsp = (union nvmf_c2h_msg *)&fc_req->ersp.rsp;
|
|
fc_req->oxid = frame->ox_id;
|
|
fc_req->oxid = from_be16(&fc_req->oxid);
|
|
fc_req->rpi = fc_conn->rpi;
|
|
fc_req->poller_lcore = hwqp->lcore_id;
|
|
fc_req->poller_thread = hwqp->thread;
|
|
fc_req->hwqp = hwqp;
|
|
fc_req->fc_conn = fc_conn;
|
|
fc_req->req.xfer = xfer;
|
|
fc_req->s_id = s_id;
|
|
fc_req->d_id = d_id;
|
|
|
|
nvmf_fc_record_req_trace_point(fc_req, SPDK_NVMF_FC_REQ_INIT);
|
|
|
|
if (!STAILQ_EMPTY(&hwqp->fgroup->group.pending_buf_queue) || nvmf_fc_request_execute(fc_req)) {
|
|
STAILQ_INSERT_TAIL(&hwqp->fgroup->group.pending_buf_queue, &fc_req->req, buf_link);
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_PENDING);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* These functions are called from the FC LLD
|
|
*/
|
|
|
|
void
|
|
_nvmf_fc_request_free(struct spdk_nvmf_fc_request *fc_req)
|
|
{
|
|
struct spdk_nvmf_fc_hwqp *hwqp = fc_req->hwqp;
|
|
struct spdk_nvmf_transport_poll_group *group;
|
|
|
|
if (!fc_req) {
|
|
return;
|
|
}
|
|
|
|
if (fc_req->xchg) {
|
|
nvmf_fc_put_xchg(hwqp, fc_req->xchg);
|
|
fc_req->xchg = NULL;
|
|
}
|
|
|
|
/* Release IO buffers */
|
|
if (fc_req->req.data_from_pool) {
|
|
group = &hwqp->fgroup->group;
|
|
spdk_nvmf_request_free_buffers(&fc_req->req, group,
|
|
group->transport);
|
|
}
|
|
fc_req->req.data = NULL;
|
|
fc_req->req.iovcnt = 0;
|
|
|
|
/* Free Fc request */
|
|
nvmf_fc_conn_free_fc_request(fc_req->fc_conn, fc_req);
|
|
}
|
|
|
|
void
|
|
nvmf_fc_request_set_state(struct spdk_nvmf_fc_request *fc_req,
|
|
enum spdk_nvmf_fc_request_state state)
|
|
{
|
|
assert(fc_req->magic != 0xDEADBEEF);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc,
|
|
"FC Request(%p):\n\tState Old:%s New:%s\n", fc_req,
|
|
nvmf_fc_request_get_state_str(fc_req->state),
|
|
nvmf_fc_request_get_state_str(state));
|
|
nvmf_fc_record_req_trace_point(fc_req, state);
|
|
fc_req->state = state;
|
|
}
|
|
|
|
char *
|
|
nvmf_fc_request_get_state_str(int state)
|
|
{
|
|
static char *unk_str = "unknown";
|
|
|
|
return (state >= 0 && state < (int)(sizeof(fc_req_state_strs) / sizeof(char *)) ?
|
|
fc_req_state_strs[state] : unk_str);
|
|
}
|
|
|
|
int
|
|
nvmf_fc_hwqp_process_frame(struct spdk_nvmf_fc_hwqp *hwqp,
|
|
uint32_t buff_idx,
|
|
struct spdk_nvmf_fc_frame_hdr *frame,
|
|
struct spdk_nvmf_fc_buffer_desc *buffer,
|
|
uint32_t plen)
|
|
{
|
|
int rc = 0;
|
|
uint32_t s_id, d_id;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
struct spdk_nvmf_fc_remote_port_info *rport = NULL;
|
|
|
|
s_id = (uint32_t)frame->s_id;
|
|
d_id = (uint32_t)frame->d_id;
|
|
s_id = from_be32(&s_id) >> 8;
|
|
d_id = from_be32(&d_id) >> 8;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc,
|
|
"Process NVME frame s_id:0x%x d_id:0x%x oxid:0x%x rxid:0x%x.\n",
|
|
s_id, d_id,
|
|
((frame->ox_id << 8) & 0xff00) | ((frame->ox_id >> 8) & 0xff),
|
|
((frame->rx_id << 8) & 0xff00) | ((frame->rx_id >> 8) & 0xff));
|
|
|
|
if ((frame->r_ctl == FCNVME_R_CTL_LS_REQUEST) &&
|
|
(frame->type == FCNVME_TYPE_NVMF_DATA)) {
|
|
struct spdk_nvmf_fc_rq_buf_ls_request *req_buf = buffer->virt;
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc, "Process LS NVME frame\n");
|
|
|
|
rc = nvmf_fc_hwqp_find_nport_and_rport(hwqp, d_id, &nport, s_id, &rport);
|
|
if (rc) {
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Nport not found. Dropping\n");
|
|
/* increment invalid nport counter */
|
|
hwqp->counters.nport_invalid++;
|
|
} else if (rport == NULL) {
|
|
SPDK_ERRLOG("Rport not found. Dropping\n");
|
|
/* increment invalid rport counter */
|
|
hwqp->counters.rport_invalid++;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
if (nport->nport_state != SPDK_NVMF_FC_OBJECT_CREATED ||
|
|
rport->rport_state != SPDK_NVMF_FC_OBJECT_CREATED) {
|
|
SPDK_ERRLOG("%s state not created. Dropping\n",
|
|
nport->nport_state != SPDK_NVMF_FC_OBJECT_CREATED ?
|
|
"Nport" : "Rport");
|
|
return -EACCES;
|
|
}
|
|
|
|
/* Use the RQ buffer for holding LS request. */
|
|
ls_rqst = (struct spdk_nvmf_fc_ls_rqst *)&req_buf->ls_rqst;
|
|
|
|
/* Fill in the LS request structure */
|
|
ls_rqst->rqstbuf.virt = (void *)&req_buf->rqst;
|
|
ls_rqst->rqstbuf.phys = buffer->phys +
|
|
offsetof(struct spdk_nvmf_fc_rq_buf_ls_request, rqst);
|
|
ls_rqst->rqstbuf.buf_index = buff_idx;
|
|
ls_rqst->rqst_len = plen;
|
|
|
|
ls_rqst->rspbuf.virt = (void *)&req_buf->resp;
|
|
ls_rqst->rspbuf.phys = buffer->phys +
|
|
offsetof(struct spdk_nvmf_fc_rq_buf_ls_request, resp);
|
|
ls_rqst->rsp_len = FCNVME_MAX_LS_RSP_SIZE;
|
|
|
|
ls_rqst->private_data = (void *)hwqp;
|
|
ls_rqst->rpi = rport->rpi;
|
|
ls_rqst->oxid = (uint16_t)frame->ox_id;
|
|
ls_rqst->oxid = from_be16(&ls_rqst->oxid);
|
|
ls_rqst->s_id = s_id;
|
|
ls_rqst->d_id = d_id;
|
|
ls_rqst->nport = nport;
|
|
ls_rqst->rport = rport;
|
|
ls_rqst->nvmf_tgt = g_nvmf_ftransport->transport.tgt;
|
|
|
|
if (TAILQ_EMPTY(&hwqp->ls_pending_queue)) {
|
|
ls_rqst->xchg = nvmf_fc_get_xri(hwqp);
|
|
} else {
|
|
ls_rqst->xchg = NULL;
|
|
}
|
|
|
|
if (ls_rqst->xchg) {
|
|
/* Handover the request to LS module */
|
|
nvmf_fc_handle_ls_rqst(ls_rqst);
|
|
} else {
|
|
/* No XCHG available. Add to pending list. */
|
|
hwqp->counters.no_xchg++;
|
|
TAILQ_INSERT_TAIL(&hwqp->ls_pending_queue, ls_rqst, ls_pending_link);
|
|
}
|
|
} else if ((frame->r_ctl == FCNVME_R_CTL_CMD_REQ) &&
|
|
(frame->type == FCNVME_TYPE_FC_EXCHANGE)) {
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc, "Process IO NVME frame\n");
|
|
rc = nvmf_fc_hwqp_handle_request(hwqp, frame, buffer, plen);
|
|
if (!rc) {
|
|
nvmf_fc_rqpair_buffer_release(hwqp, buff_idx);
|
|
}
|
|
} else {
|
|
|
|
SPDK_ERRLOG("Unknown frame received. Dropping\n");
|
|
hwqp->counters.unknown_frame++;
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
nvmf_fc_hwqp_process_pending_reqs(struct spdk_nvmf_fc_hwqp *hwqp)
|
|
{
|
|
struct spdk_nvmf_request *req = NULL, *tmp;
|
|
struct spdk_nvmf_fc_request *fc_req;
|
|
int budget = 64;
|
|
|
|
if (!hwqp->fgroup) {
|
|
/* LS queue is tied to acceptor_poll group and LS pending requests
|
|
* are stagged and processed using hwqp->ls_pending_queue.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
STAILQ_FOREACH_SAFE(req, &hwqp->fgroup->group.pending_buf_queue, buf_link, tmp) {
|
|
fc_req = SPDK_CONTAINEROF(req, struct spdk_nvmf_fc_request, req);
|
|
if (!nvmf_fc_request_execute(fc_req)) {
|
|
/* Succesfuly posted, Delete from pending. */
|
|
nvmf_fc_request_remove_from_pending(fc_req);
|
|
}
|
|
|
|
if (budget) {
|
|
budget--;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nvmf_fc_hwqp_process_pending_ls_rqsts(struct spdk_nvmf_fc_hwqp *hwqp)
|
|
{
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst = NULL, *tmp;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
struct spdk_nvmf_fc_remote_port_info *rport = NULL;
|
|
|
|
TAILQ_FOREACH_SAFE(ls_rqst, &hwqp->ls_pending_queue, ls_pending_link, tmp) {
|
|
/* lookup nport and rport again - make sure they are still valid */
|
|
int rc = nvmf_fc_hwqp_find_nport_and_rport(hwqp, ls_rqst->d_id, &nport, ls_rqst->s_id, &rport);
|
|
if (rc) {
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Nport not found. Dropping\n");
|
|
/* increment invalid nport counter */
|
|
hwqp->counters.nport_invalid++;
|
|
} else if (rport == NULL) {
|
|
SPDK_ERRLOG("Rport not found. Dropping\n");
|
|
/* increment invalid rport counter */
|
|
hwqp->counters.rport_invalid++;
|
|
}
|
|
TAILQ_REMOVE(&hwqp->ls_pending_queue, ls_rqst, ls_pending_link);
|
|
/* Return buffer to chip */
|
|
nvmf_fc_rqpair_buffer_release(hwqp, ls_rqst->rqstbuf.buf_index);
|
|
continue;
|
|
}
|
|
if (nport->nport_state != SPDK_NVMF_FC_OBJECT_CREATED ||
|
|
rport->rport_state != SPDK_NVMF_FC_OBJECT_CREATED) {
|
|
SPDK_ERRLOG("%s state not created. Dropping\n",
|
|
nport->nport_state != SPDK_NVMF_FC_OBJECT_CREATED ?
|
|
"Nport" : "Rport");
|
|
TAILQ_REMOVE(&hwqp->ls_pending_queue, ls_rqst, ls_pending_link);
|
|
/* Return buffer to chip */
|
|
nvmf_fc_rqpair_buffer_release(hwqp, ls_rqst->rqstbuf.buf_index);
|
|
continue;
|
|
}
|
|
|
|
ls_rqst->xchg = nvmf_fc_get_xri(hwqp);
|
|
if (ls_rqst->xchg) {
|
|
/* Got an XCHG */
|
|
TAILQ_REMOVE(&hwqp->ls_pending_queue, ls_rqst, ls_pending_link);
|
|
/* Handover the request to LS module */
|
|
nvmf_fc_handle_ls_rqst(ls_rqst);
|
|
} else {
|
|
/* No more XCHGs. Stop processing. */
|
|
hwqp->counters.no_xchg++;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
nvmf_fc_handle_rsp(struct spdk_nvmf_fc_request *fc_req)
|
|
{
|
|
int rc = 0;
|
|
struct spdk_nvmf_request *req = &fc_req->req;
|
|
struct spdk_nvmf_qpair *qpair = req->qpair;
|
|
struct spdk_nvmf_fc_conn *fc_conn = nvmf_fc_get_conn(qpair);
|
|
struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
|
|
uint16_t ersp_len = 0;
|
|
|
|
/* set sq head value in resp */
|
|
rsp->sqhd = nvmf_fc_advance_conn_sqhead(qpair);
|
|
|
|
/* Increment connection responses */
|
|
fc_conn->rsp_count++;
|
|
|
|
if (nvmf_fc_send_ersp_required(fc_req, fc_conn->rsp_count,
|
|
fc_req->transfered_len)) {
|
|
/* Fill ERSP Len */
|
|
to_be16(&ersp_len, (sizeof(struct spdk_nvmf_fc_ersp_iu) /
|
|
sizeof(uint32_t)));
|
|
fc_req->ersp.ersp_len = ersp_len;
|
|
|
|
/* Fill RSN */
|
|
to_be32(&fc_req->ersp.response_seq_no, fc_conn->rsn);
|
|
fc_conn->rsn++;
|
|
|
|
/* Fill transfer length */
|
|
to_be32(&fc_req->ersp.transferred_data_len, fc_req->transfered_len);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc, "Posting ERSP.\n");
|
|
rc = nvmf_fc_xmt_rsp(fc_req, (uint8_t *)&fc_req->ersp,
|
|
sizeof(struct spdk_nvmf_fc_ersp_iu));
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc, "Posting RSP.\n");
|
|
rc = nvmf_fc_xmt_rsp(fc_req, NULL, 0);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool
|
|
nvmf_fc_send_ersp_required(struct spdk_nvmf_fc_request *fc_req,
|
|
uint32_t rsp_cnt, uint32_t xfer_len)
|
|
{
|
|
struct spdk_nvmf_request *req = &fc_req->req;
|
|
struct spdk_nvmf_qpair *qpair = req->qpair;
|
|
struct spdk_nvmf_fc_conn *fc_conn = nvmf_fc_get_conn(qpair);
|
|
struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
|
|
struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
|
|
uint16_t status = *((uint16_t *)&rsp->status);
|
|
|
|
/*
|
|
* Check if we need to send ERSP
|
|
* 1) For every N responses where N == ersp_ratio
|
|
* 2) Fabric commands.
|
|
* 3) Completion status failed or Completion dw0 or dw1 valid.
|
|
* 4) SQ == 90% full.
|
|
* 5) Transfer length not equal to CMD IU length
|
|
*/
|
|
|
|
if (!(rsp_cnt % fc_conn->esrp_ratio) ||
|
|
(cmd->opc == SPDK_NVME_OPC_FABRIC) ||
|
|
(status & 0xFFFE) || rsp->cdw0 || rsp->rsvd1 ||
|
|
(req->length != xfer_len)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_request_complete(struct spdk_nvmf_request *req)
|
|
{
|
|
int rc = 0;
|
|
struct spdk_nvmf_fc_request *fc_req = nvmf_fc_get_fc_req(req);
|
|
struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
|
|
|
|
if (fc_req->is_aborted) {
|
|
/* Defer this to make sure we dont call io cleanup in same context. */
|
|
nvmf_fc_poller_api_func(fc_req->hwqp, SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE,
|
|
(void *)fc_req);
|
|
} else if (rsp->status.sc == SPDK_NVME_SC_SUCCESS &&
|
|
req->xfer == SPDK_NVME_DATA_CONTROLLER_TO_HOST) {
|
|
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_READ_XFER);
|
|
|
|
rc = nvmf_fc_send_data(fc_req);
|
|
} else {
|
|
if (req->xfer == SPDK_NVME_DATA_HOST_TO_CONTROLLER) {
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_WRITE_RSP);
|
|
} else if (req->xfer == SPDK_NVME_DATA_CONTROLLER_TO_HOST) {
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_READ_RSP);
|
|
} else {
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_NONE_RSP);
|
|
}
|
|
|
|
rc = nvmf_fc_handle_rsp(fc_req);
|
|
}
|
|
|
|
if (rc) {
|
|
SPDK_ERRLOG("Error in request complete.\n");
|
|
_nvmf_fc_request_free(fc_req);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct spdk_nvmf_tgt *
|
|
nvmf_fc_get_tgt(void)
|
|
{
|
|
if (g_nvmf_ftransport) {
|
|
return g_nvmf_ftransport->transport.tgt;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* FC Transport Public API begins here
|
|
*/
|
|
|
|
#define SPDK_NVMF_FC_DEFAULT_MAX_QUEUE_DEPTH 128
|
|
#define SPDK_NVMF_FC_DEFAULT_AQ_DEPTH 32
|
|
#define SPDK_NVMF_FC_DEFAULT_MAX_QPAIRS_PER_CTRLR 5
|
|
#define SPDK_NVMF_FC_DEFAULT_IN_CAPSULE_DATA_SIZE 0
|
|
#define SPDK_NVMF_FC_DEFAULT_MAX_IO_SIZE 65536
|
|
#define SPDK_NVMF_FC_DEFAULT_IO_UNIT_SIZE 4096
|
|
#define SPDK_NVMF_FC_DEFAULT_NUM_SHARED_BUFFERS 8192
|
|
#define SPDK_NVMF_FC_DEFAULT_MAX_SGE (SPDK_NVMF_FC_DEFAULT_MAX_IO_SIZE / \
|
|
SPDK_NVMF_FC_DEFAULT_IO_UNIT_SIZE)
|
|
|
|
static void
|
|
nvmf_fc_opts_init(struct spdk_nvmf_transport_opts *opts)
|
|
{
|
|
opts->max_queue_depth = SPDK_NVMF_FC_DEFAULT_MAX_QUEUE_DEPTH;
|
|
opts->max_qpairs_per_ctrlr = SPDK_NVMF_FC_DEFAULT_MAX_QPAIRS_PER_CTRLR;
|
|
opts->in_capsule_data_size = SPDK_NVMF_FC_DEFAULT_IN_CAPSULE_DATA_SIZE;
|
|
opts->max_io_size = SPDK_NVMF_FC_DEFAULT_MAX_IO_SIZE;
|
|
opts->io_unit_size = SPDK_NVMF_FC_DEFAULT_IO_UNIT_SIZE;
|
|
opts->max_aq_depth = SPDK_NVMF_FC_DEFAULT_AQ_DEPTH;
|
|
opts->num_shared_buffers = SPDK_NVMF_FC_DEFAULT_NUM_SHARED_BUFFERS;
|
|
}
|
|
|
|
static struct spdk_nvmf_transport *
|
|
nvmf_fc_create(struct spdk_nvmf_transport_opts *opts)
|
|
{
|
|
uint32_t sge_count;
|
|
|
|
SPDK_INFOLOG(nvmf_fc, "*** FC Transport Init ***\n"
|
|
" Transport opts: max_ioq_depth=%d, max_io_size=%d,\n"
|
|
" max_io_qpairs_per_ctrlr=%d, io_unit_size=%d,\n"
|
|
" max_aq_depth=%d\n",
|
|
opts->max_queue_depth,
|
|
opts->max_io_size,
|
|
opts->max_qpairs_per_ctrlr - 1,
|
|
opts->io_unit_size,
|
|
opts->max_aq_depth);
|
|
|
|
if (g_nvmf_ftransport) {
|
|
SPDK_ERRLOG("Duplicate NVMF-FC transport create request!\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (spdk_env_get_last_core() < 1) {
|
|
SPDK_ERRLOG("Not enough cores/threads (%d) to run NVMF-FC transport!\n",
|
|
spdk_env_get_last_core() + 1);
|
|
return NULL;
|
|
}
|
|
|
|
sge_count = opts->max_io_size / opts->io_unit_size;
|
|
if (sge_count > SPDK_NVMF_FC_DEFAULT_MAX_SGE) {
|
|
SPDK_ERRLOG("Unsupported IO Unit size specified, %d bytes\n", opts->io_unit_size);
|
|
return NULL;
|
|
}
|
|
|
|
g_nvmf_fc_main_thread = spdk_get_thread();
|
|
g_nvmf_fgroup_count = 0;
|
|
g_nvmf_ftransport = calloc(1, sizeof(*g_nvmf_ftransport));
|
|
|
|
if (!g_nvmf_ftransport) {
|
|
SPDK_ERRLOG("Failed to allocate NVMF-FC transport\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_mutex_init(&g_nvmf_ftransport->lock, NULL)) {
|
|
SPDK_ERRLOG("pthread_mutex_init() failed\n");
|
|
free(g_nvmf_ftransport);
|
|
g_nvmf_ftransport = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
/* initialize the low level FC driver */
|
|
nvmf_fc_lld_init();
|
|
|
|
return &g_nvmf_ftransport->transport;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_destroy(struct spdk_nvmf_transport *transport,
|
|
spdk_nvmf_transport_destroy_done_cb cb_fn, void *cb_arg)
|
|
{
|
|
if (transport) {
|
|
struct spdk_nvmf_fc_transport *ftransport;
|
|
struct spdk_nvmf_fc_poll_group *fgroup, *pg_tmp;
|
|
|
|
ftransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_fc_transport, transport);
|
|
|
|
free(ftransport);
|
|
|
|
/* clean up any FC poll groups still around */
|
|
TAILQ_FOREACH_SAFE(fgroup, &g_nvmf_fgroups, link, pg_tmp) {
|
|
TAILQ_REMOVE(&g_nvmf_fgroups, fgroup, link);
|
|
free(fgroup);
|
|
}
|
|
g_nvmf_fgroup_count = 0;
|
|
|
|
/* low level FC driver clean up */
|
|
nvmf_fc_lld_fini();
|
|
|
|
nvmf_fc_port_cleanup();
|
|
}
|
|
|
|
if (cb_fn) {
|
|
cb_fn(cb_arg);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_listen(struct spdk_nvmf_transport *transport, const struct spdk_nvme_transport_id *trid,
|
|
struct spdk_nvmf_listen_opts *listen_opts)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_stop_listen(struct spdk_nvmf_transport *transport,
|
|
const struct spdk_nvme_transport_id *_trid)
|
|
{
|
|
}
|
|
|
|
static uint32_t
|
|
nvmf_fc_accept(struct spdk_nvmf_transport *transport)
|
|
{
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
uint32_t count = 0;
|
|
static bool start_lld = false;
|
|
|
|
if (spdk_unlikely(!start_lld)) {
|
|
start_lld = true;
|
|
nvmf_fc_lld_start();
|
|
}
|
|
|
|
/* poll the LS queue on each port */
|
|
TAILQ_FOREACH(fc_port, &g_spdk_nvmf_fc_port_list, link) {
|
|
if (fc_port->hw_port_status == SPDK_FC_PORT_ONLINE) {
|
|
count += nvmf_fc_process_queue(&fc_port->ls_queue);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_discover(struct spdk_nvmf_transport *transport,
|
|
struct spdk_nvme_transport_id *trid,
|
|
struct spdk_nvmf_discovery_log_page_entry *entry)
|
|
{
|
|
entry->trtype = (enum spdk_nvme_transport_type) SPDK_NVMF_TRTYPE_FC;
|
|
entry->adrfam = trid->adrfam;
|
|
entry->treq.secure_channel = SPDK_NVMF_TREQ_SECURE_CHANNEL_NOT_SPECIFIED;
|
|
|
|
spdk_strcpy_pad(entry->trsvcid, trid->trsvcid, sizeof(entry->trsvcid), ' ');
|
|
spdk_strcpy_pad(entry->traddr, trid->traddr, sizeof(entry->traddr), ' ');
|
|
}
|
|
|
|
static struct spdk_nvmf_transport_poll_group *
|
|
nvmf_fc_poll_group_create(struct spdk_nvmf_transport *transport)
|
|
{
|
|
struct spdk_nvmf_fc_poll_group *fgroup;
|
|
struct spdk_nvmf_fc_transport *ftransport =
|
|
SPDK_CONTAINEROF(transport, struct spdk_nvmf_fc_transport, transport);
|
|
|
|
fgroup = calloc(1, sizeof(struct spdk_nvmf_fc_poll_group));
|
|
if (!fgroup) {
|
|
SPDK_ERRLOG("Unable to alloc FC poll group\n");
|
|
return NULL;
|
|
}
|
|
|
|
TAILQ_INIT(&fgroup->hwqp_list);
|
|
|
|
pthread_mutex_lock(&ftransport->lock);
|
|
TAILQ_INSERT_TAIL(&g_nvmf_fgroups, fgroup, link);
|
|
g_nvmf_fgroup_count++;
|
|
pthread_mutex_unlock(&ftransport->lock);
|
|
|
|
return &fgroup->group;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poll_group_destroy(struct spdk_nvmf_transport_poll_group *group)
|
|
{
|
|
struct spdk_nvmf_fc_poll_group *fgroup;
|
|
struct spdk_nvmf_fc_transport *ftransport =
|
|
SPDK_CONTAINEROF(group->transport, struct spdk_nvmf_fc_transport, transport);
|
|
|
|
fgroup = SPDK_CONTAINEROF(group, struct spdk_nvmf_fc_poll_group, group);
|
|
pthread_mutex_lock(&ftransport->lock);
|
|
TAILQ_REMOVE(&g_nvmf_fgroups, fgroup, link);
|
|
g_nvmf_fgroup_count--;
|
|
pthread_mutex_unlock(&ftransport->lock);
|
|
|
|
free(fgroup);
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_poll_group_add(struct spdk_nvmf_transport_poll_group *group,
|
|
struct spdk_nvmf_qpair *qpair)
|
|
{
|
|
struct spdk_nvmf_fc_poll_group *fgroup;
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = NULL;
|
|
struct spdk_nvmf_fc_ls_add_conn_api_data *api_data = NULL;
|
|
bool hwqp_found = false;
|
|
|
|
fgroup = SPDK_CONTAINEROF(group, struct spdk_nvmf_fc_poll_group, group);
|
|
fc_conn = SPDK_CONTAINEROF(qpair, struct spdk_nvmf_fc_conn, qpair);
|
|
|
|
TAILQ_FOREACH(hwqp, &fgroup->hwqp_list, link) {
|
|
if (fc_conn->fc_assoc->tgtport->fc_port == hwqp->fc_port) {
|
|
hwqp_found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hwqp_found) {
|
|
SPDK_ERRLOG("No valid hwqp found for new QP.\n");
|
|
goto err;
|
|
}
|
|
|
|
if (!nvmf_fc_assign_conn_to_hwqp(hwqp,
|
|
&fc_conn->conn_id,
|
|
fc_conn->max_queue_depth)) {
|
|
SPDK_ERRLOG("Failed to get a connection id for new QP.\n");
|
|
goto err;
|
|
}
|
|
|
|
fc_conn->hwqp = hwqp;
|
|
|
|
/* If this is for ADMIN connection, then update assoc ID. */
|
|
if (fc_conn->qpair.qid == 0) {
|
|
fc_conn->fc_assoc->assoc_id = fc_conn->conn_id;
|
|
}
|
|
|
|
api_data = &fc_conn->create_opd->u.add_conn;
|
|
nvmf_fc_poller_api_func(hwqp, SPDK_NVMF_FC_POLLER_API_ADD_CONNECTION, &api_data->args);
|
|
return 0;
|
|
err:
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_poll_group_poll(struct spdk_nvmf_transport_poll_group *group)
|
|
{
|
|
uint32_t count = 0;
|
|
struct spdk_nvmf_fc_poll_group *fgroup;
|
|
struct spdk_nvmf_fc_hwqp *hwqp;
|
|
|
|
fgroup = SPDK_CONTAINEROF(group, struct spdk_nvmf_fc_poll_group, group);
|
|
|
|
TAILQ_FOREACH(hwqp, &fgroup->hwqp_list, link) {
|
|
if (hwqp->state == SPDK_FC_HWQP_ONLINE) {
|
|
count += nvmf_fc_process_queue(hwqp);
|
|
}
|
|
}
|
|
|
|
return (int) count;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_request_free(struct spdk_nvmf_request *req)
|
|
{
|
|
struct spdk_nvmf_fc_request *fc_req = nvmf_fc_get_fc_req(req);
|
|
|
|
if (!fc_req->is_aborted) {
|
|
nvmf_fc_request_set_state(fc_req, SPDK_NVMF_FC_REQ_BDEV_ABORTED);
|
|
nvmf_fc_request_abort(fc_req, true, NULL, NULL);
|
|
} else {
|
|
nvmf_fc_request_abort_complete(fc_req);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_close_qpair(struct spdk_nvmf_qpair *qpair,
|
|
spdk_nvmf_transport_qpair_fini_cb cb_fn, void *cb_arg)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
fc_conn = SPDK_CONTAINEROF(qpair, struct spdk_nvmf_fc_conn, qpair);
|
|
|
|
if (fc_conn->conn_id == NVMF_FC_INVALID_CONN_ID) {
|
|
/* QP creation failure in FC tranport. Cleanup. */
|
|
spdk_thread_send_msg(nvmf_fc_get_main_thread(),
|
|
nvmf_fc_handle_connection_failure, fc_conn);
|
|
} else if (fc_conn->fc_assoc->assoc_id == fc_conn->conn_id &&
|
|
fc_conn->fc_assoc->assoc_state != SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) {
|
|
/* Admin connection */
|
|
spdk_thread_send_msg(nvmf_fc_get_main_thread(),
|
|
nvmf_fc_handle_assoc_deletion, fc_conn);
|
|
}
|
|
|
|
if (cb_fn) {
|
|
cb_fn(cb_arg);
|
|
}
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_qpair_get_peer_trid(struct spdk_nvmf_qpair *qpair,
|
|
struct spdk_nvme_transport_id *trid)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
fc_conn = SPDK_CONTAINEROF(qpair, struct spdk_nvmf_fc_conn, qpair);
|
|
memcpy(trid, &fc_conn->trid, sizeof(struct spdk_nvme_transport_id));
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_qpair_get_local_trid(struct spdk_nvmf_qpair *qpair,
|
|
struct spdk_nvme_transport_id *trid)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
fc_conn = SPDK_CONTAINEROF(qpair, struct spdk_nvmf_fc_conn, qpair);
|
|
memcpy(trid, &fc_conn->trid, sizeof(struct spdk_nvme_transport_id));
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_qpair_get_listen_trid(struct spdk_nvmf_qpair *qpair,
|
|
struct spdk_nvme_transport_id *trid)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
fc_conn = SPDK_CONTAINEROF(qpair, struct spdk_nvmf_fc_conn, qpair);
|
|
memcpy(trid, &fc_conn->trid, sizeof(struct spdk_nvme_transport_id));
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_qpair_abort_request(struct spdk_nvmf_qpair *qpair,
|
|
struct spdk_nvmf_request *req)
|
|
{
|
|
spdk_nvmf_request_complete(req);
|
|
}
|
|
|
|
const struct spdk_nvmf_transport_ops spdk_nvmf_transport_fc = {
|
|
.name = "FC",
|
|
.type = (enum spdk_nvme_transport_type) SPDK_NVMF_TRTYPE_FC,
|
|
.opts_init = nvmf_fc_opts_init,
|
|
.create = nvmf_fc_create,
|
|
.destroy = nvmf_fc_destroy,
|
|
|
|
.listen = nvmf_fc_listen,
|
|
.stop_listen = nvmf_fc_stop_listen,
|
|
.accept = nvmf_fc_accept,
|
|
|
|
.listener_discover = nvmf_fc_discover,
|
|
|
|
.poll_group_create = nvmf_fc_poll_group_create,
|
|
.poll_group_destroy = nvmf_fc_poll_group_destroy,
|
|
.poll_group_add = nvmf_fc_poll_group_add,
|
|
.poll_group_poll = nvmf_fc_poll_group_poll,
|
|
|
|
.req_complete = nvmf_fc_request_complete,
|
|
.req_free = nvmf_fc_request_free,
|
|
.qpair_fini = nvmf_fc_close_qpair,
|
|
.qpair_get_peer_trid = nvmf_fc_qpair_get_peer_trid,
|
|
.qpair_get_local_trid = nvmf_fc_qpair_get_local_trid,
|
|
.qpair_get_listen_trid = nvmf_fc_qpair_get_listen_trid,
|
|
.qpair_abort_request = nvmf_fc_qpair_abort_request,
|
|
};
|
|
|
|
/* Initializes the data for the creation of a FC-Port object in the SPDK
|
|
* library. The spdk_nvmf_fc_port is a well defined structure that is part of
|
|
* the API to the library. The contents added to this well defined structure
|
|
* is private to each vendors implementation.
|
|
*/
|
|
static int
|
|
nvmf_fc_adm_hw_port_data_init(struct spdk_nvmf_fc_port *fc_port,
|
|
struct spdk_nvmf_fc_hw_port_init_args *args)
|
|
{
|
|
int rc = 0;
|
|
/* Used a high number for the LS HWQP so that it does not clash with the
|
|
* IO HWQP's and immediately shows a LS queue during tracing.
|
|
*/
|
|
uint32_t i;
|
|
|
|
fc_port->port_hdl = args->port_handle;
|
|
fc_port->hw_port_status = SPDK_FC_PORT_OFFLINE;
|
|
fc_port->fcp_rq_id = args->fcp_rq_id;
|
|
fc_port->num_io_queues = args->io_queue_cnt;
|
|
|
|
/*
|
|
* Set port context from init args. Used for FCP port stats.
|
|
*/
|
|
fc_port->port_ctx = args->port_ctx;
|
|
|
|
/*
|
|
* Initialize the LS queue wherever needed.
|
|
*/
|
|
fc_port->ls_queue.queues = args->ls_queue;
|
|
fc_port->ls_queue.thread = nvmf_fc_get_main_thread();
|
|
fc_port->ls_queue.hwqp_id = SPDK_MAX_NUM_OF_FC_PORTS * fc_port->num_io_queues;
|
|
|
|
/*
|
|
* Initialize the LS queue.
|
|
*/
|
|
rc = nvmf_fc_init_hwqp(fc_port, &fc_port->ls_queue);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Initialize the IO queues.
|
|
*/
|
|
for (i = 0; i < args->io_queue_cnt; i++) {
|
|
struct spdk_nvmf_fc_hwqp *hwqp = &fc_port->io_queues[i];
|
|
hwqp->hwqp_id = i;
|
|
hwqp->queues = args->io_queues[i];
|
|
rc = nvmf_fc_init_hwqp(fc_port, hwqp);
|
|
if (rc) {
|
|
for (; i > 0; --i) {
|
|
rte_hash_free(fc_port->io_queues[i - 1].connection_list_hash);
|
|
rte_hash_free(fc_port->io_queues[i - 1].rport_list_hash);
|
|
}
|
|
rte_hash_free(fc_port->ls_queue.connection_list_hash);
|
|
rte_hash_free(fc_port->ls_queue.rport_list_hash);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the LS processing for port
|
|
*/
|
|
nvmf_fc_ls_init(fc_port);
|
|
|
|
/*
|
|
* Initialize the list of nport on this HW port.
|
|
*/
|
|
TAILQ_INIT(&fc_port->nport_list);
|
|
fc_port->num_nports = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* FC port must have all its nports deleted before transitioning to offline state.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_hw_port_offline_nport_delete(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
/* All nports must have been deleted at this point for this fc port */
|
|
DEV_VERIFY(fc_port && TAILQ_EMPTY(&fc_port->nport_list));
|
|
DEV_VERIFY(fc_port->num_nports == 0);
|
|
/* Mark the nport states to be zombie, if they exist */
|
|
if (fc_port && !TAILQ_EMPTY(&fc_port->nport_list)) {
|
|
TAILQ_FOREACH(nport, &fc_port->nport_list, link) {
|
|
(void)nvmf_fc_nport_set_state(nport, SPDK_NVMF_FC_OBJECT_ZOMBIE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_adm_i_t_delete_cb(void *args, uint32_t err)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_i_t_del_cb_data *cb_data = args;
|
|
struct spdk_nvmf_fc_nport *nport = cb_data->nport;
|
|
struct spdk_nvmf_fc_remote_port_info *rport = cb_data->rport;
|
|
spdk_nvmf_fc_callback cb_func = cb_data->fc_cb_func;
|
|
int spdk_err = 0;
|
|
uint8_t port_handle = cb_data->port_handle;
|
|
uint32_t s_id = rport->s_id;
|
|
uint32_t rpi = rport->rpi;
|
|
uint32_t assoc_count = rport->assoc_count;
|
|
uint32_t nport_hdl = nport->nport_hdl;
|
|
uint32_t d_id = nport->d_id;
|
|
char log_str[256];
|
|
|
|
/*
|
|
* Assert on any delete failure.
|
|
*/
|
|
if (0 != err) {
|
|
DEV_VERIFY(!"Error in IT Delete callback.");
|
|
goto out;
|
|
}
|
|
|
|
if (cb_func != NULL) {
|
|
(void)cb_func(port_handle, SPDK_FC_IT_DELETE, cb_data->fc_cb_ctx, spdk_err);
|
|
}
|
|
|
|
out:
|
|
free(cb_data);
|
|
|
|
snprintf(log_str, sizeof(log_str),
|
|
"IT delete assoc_cb on nport %d done, port_handle:%d s_id:%d d_id:%d rpi:%d rport_assoc_count:%d rc = %d.\n",
|
|
nport_hdl, port_handle, s_id, d_id, rpi, assoc_count, err);
|
|
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("%s", log_str);
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "%s", log_str);
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_adm_i_t_delete_assoc_cb(void *args, uint32_t err)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_i_t_del_assoc_cb_data *cb_data = args;
|
|
struct spdk_nvmf_fc_nport *nport = cb_data->nport;
|
|
struct spdk_nvmf_fc_remote_port_info *rport = cb_data->rport;
|
|
spdk_nvmf_fc_adm_i_t_delete_assoc_cb_fn cb_func = cb_data->cb_func;
|
|
uint32_t s_id = rport->s_id;
|
|
uint32_t rpi = rport->rpi;
|
|
uint32_t assoc_count = rport->assoc_count;
|
|
uint32_t nport_hdl = nport->nport_hdl;
|
|
uint32_t d_id = nport->d_id;
|
|
char log_str[256];
|
|
|
|
/*
|
|
* Assert on any association delete failure. We continue to delete other
|
|
* associations in promoted builds.
|
|
*/
|
|
if (0 != err) {
|
|
DEV_VERIFY(!"Nport's association delete callback returned error");
|
|
if (nport->assoc_count > 0) {
|
|
nport->assoc_count--;
|
|
}
|
|
if (rport->assoc_count > 0) {
|
|
rport->assoc_count--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this is the last association being deleted for the ITN,
|
|
* execute the callback(s).
|
|
*/
|
|
if (0 == rport->assoc_count) {
|
|
/* Remove the rport from the remote port list. */
|
|
if (nvmf_fc_nport_remove_rem_port(nport, rport) != 0) {
|
|
SPDK_ERRLOG("Error while removing rport from list.\n");
|
|
DEV_VERIFY(!"Error while removing rport from list.");
|
|
}
|
|
|
|
if (cb_func != NULL) {
|
|
/*
|
|
* Callback function is provided by the caller
|
|
* of nvmf_fc_adm_i_t_delete_assoc().
|
|
*/
|
|
(void)cb_func(cb_data->cb_ctx, 0);
|
|
}
|
|
free(rport);
|
|
free(args);
|
|
}
|
|
|
|
snprintf(log_str, sizeof(log_str),
|
|
"IT delete assoc_cb on nport %d done, s_id:%d d_id:%d rpi:%d rport_assoc_count:%d err = %d.\n",
|
|
nport_hdl, s_id, d_id, rpi, assoc_count, err);
|
|
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("%s", log_str);
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "%s", log_str);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a IT delete.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_i_t_delete_assoc(struct spdk_nvmf_fc_nport *nport,
|
|
struct spdk_nvmf_fc_remote_port_info *rport,
|
|
spdk_nvmf_fc_adm_i_t_delete_assoc_cb_fn cb_func,
|
|
void *cb_ctx)
|
|
{
|
|
int err = 0;
|
|
struct spdk_nvmf_fc_association *assoc = NULL;
|
|
int assoc_err = 0;
|
|
uint32_t num_assoc = 0;
|
|
uint32_t num_assoc_del_scheduled = 0;
|
|
struct spdk_nvmf_fc_adm_i_t_del_assoc_cb_data *cb_data = NULL;
|
|
uint8_t port_hdl = nport->port_hdl;
|
|
uint32_t s_id = rport->s_id;
|
|
uint32_t rpi = rport->rpi;
|
|
uint32_t assoc_count = rport->assoc_count;
|
|
char log_str[256];
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "IT delete associations on nport:%d begin.\n",
|
|
nport->nport_hdl);
|
|
|
|
/*
|
|
* Allocate memory for callback data.
|
|
* This memory will be freed by the callback function.
|
|
*/
|
|
cb_data = calloc(1, sizeof(struct spdk_nvmf_fc_adm_i_t_del_assoc_cb_data));
|
|
if (NULL == cb_data) {
|
|
SPDK_ERRLOG("Failed to allocate memory for cb_data on nport:%d.\n", nport->nport_hdl);
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
cb_data->nport = nport;
|
|
cb_data->rport = rport;
|
|
cb_data->port_handle = port_hdl;
|
|
cb_data->cb_func = cb_func;
|
|
cb_data->cb_ctx = cb_ctx;
|
|
|
|
/*
|
|
* Delete all associations, if any, related with this ITN/remote_port.
|
|
*/
|
|
TAILQ_FOREACH(assoc, &nport->fc_associations, link) {
|
|
num_assoc++;
|
|
if (assoc->s_id == s_id) {
|
|
assoc_err = nvmf_fc_delete_association(nport,
|
|
assoc->assoc_id,
|
|
false /* send abts */, false,
|
|
nvmf_fc_adm_i_t_delete_assoc_cb, cb_data);
|
|
if (0 != assoc_err) {
|
|
/*
|
|
* Mark this association as zombie.
|
|
*/
|
|
err = -EINVAL;
|
|
DEV_VERIFY(!"Error while deleting association");
|
|
(void)nvmf_fc_assoc_set_state(assoc, SPDK_NVMF_FC_OBJECT_ZOMBIE);
|
|
} else {
|
|
num_assoc_del_scheduled++;
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
if ((cb_data) && (num_assoc_del_scheduled == 0)) {
|
|
/*
|
|
* Since there are no association_delete calls
|
|
* successfully scheduled, the association_delete
|
|
* callback function will never be called.
|
|
* In this case, call the callback function now.
|
|
*/
|
|
nvmf_fc_adm_i_t_delete_assoc_cb(cb_data, 0);
|
|
}
|
|
|
|
snprintf(log_str, sizeof(log_str),
|
|
"IT delete associations on nport:%d end. "
|
|
"s_id:%d rpi:%d assoc_count:%d assoc:%d assoc_del_scheduled:%d rc:%d.\n",
|
|
nport->nport_hdl, s_id, rpi, assoc_count, num_assoc, num_assoc_del_scheduled, err);
|
|
|
|
if (err == 0) {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "%s", log_str);
|
|
} else {
|
|
SPDK_ERRLOG("%s", log_str);
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_adm_queue_quiesce_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_poller_api_quiesce_queue_args *quiesce_api_data = NULL;
|
|
struct spdk_nvmf_fc_adm_hw_port_quiesce_ctx *port_quiesce_ctx = NULL;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = NULL;
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
int err = 0;
|
|
|
|
quiesce_api_data = (struct spdk_nvmf_fc_poller_api_quiesce_queue_args *)cb_data;
|
|
hwqp = quiesce_api_data->hwqp;
|
|
fc_port = hwqp->fc_port;
|
|
port_quiesce_ctx = (struct spdk_nvmf_fc_adm_hw_port_quiesce_ctx *)quiesce_api_data->ctx;
|
|
spdk_nvmf_fc_adm_hw_port_quiesce_cb_fn cb_func = port_quiesce_ctx->cb_func;
|
|
|
|
/*
|
|
* Decrement the callback/quiesced queue count.
|
|
*/
|
|
port_quiesce_ctx->quiesce_count--;
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "Queue%d Quiesced\n", quiesce_api_data->hwqp->hwqp_id);
|
|
|
|
free(quiesce_api_data);
|
|
/*
|
|
* Wait for call backs i.e. max_ioq_queues + LS QUEUE.
|
|
*/
|
|
if (port_quiesce_ctx->quiesce_count > 0) {
|
|
return;
|
|
}
|
|
|
|
if (fc_port->hw_port_status == SPDK_FC_PORT_QUIESCED) {
|
|
SPDK_ERRLOG("Port %d already in quiesced state.\n", fc_port->port_hdl);
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d quiesced.\n", fc_port->port_hdl);
|
|
fc_port->hw_port_status = SPDK_FC_PORT_QUIESCED;
|
|
}
|
|
|
|
if (cb_func) {
|
|
/*
|
|
* Callback function for the called of quiesce.
|
|
*/
|
|
cb_func(port_quiesce_ctx->ctx, err);
|
|
}
|
|
|
|
/*
|
|
* Free the context structure.
|
|
*/
|
|
free(port_quiesce_ctx);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d quiesce done, rc = %d.\n", fc_port->port_hdl,
|
|
err);
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_adm_hw_queue_quiesce(struct spdk_nvmf_fc_hwqp *fc_hwqp, void *ctx,
|
|
spdk_nvmf_fc_poller_api_cb cb_func)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_quiesce_queue_args *args;
|
|
enum spdk_nvmf_fc_poller_api_ret rc = SPDK_NVMF_FC_POLLER_API_SUCCESS;
|
|
int err = 0;
|
|
|
|
args = calloc(1, sizeof(struct spdk_nvmf_fc_poller_api_quiesce_queue_args));
|
|
|
|
if (args == NULL) {
|
|
err = -ENOMEM;
|
|
SPDK_ERRLOG("Failed to allocate memory for poller quiesce args, hwqp:%d\n", fc_hwqp->hwqp_id);
|
|
goto done;
|
|
}
|
|
args->hwqp = fc_hwqp;
|
|
args->ctx = ctx;
|
|
args->cb_info.cb_func = cb_func;
|
|
args->cb_info.cb_data = args;
|
|
args->cb_info.cb_thread = spdk_get_thread();
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "Quiesce queue %d\n", fc_hwqp->hwqp_id);
|
|
rc = nvmf_fc_poller_api_func(fc_hwqp, SPDK_NVMF_FC_POLLER_API_QUIESCE_QUEUE, args);
|
|
if (rc) {
|
|
free(args);
|
|
err = -EINVAL;
|
|
}
|
|
|
|
done:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Hw port Quiesce
|
|
*/
|
|
static int
|
|
nvmf_fc_adm_hw_port_quiesce(struct spdk_nvmf_fc_port *fc_port, void *ctx,
|
|
spdk_nvmf_fc_adm_hw_port_quiesce_cb_fn cb_func)
|
|
{
|
|
struct spdk_nvmf_fc_adm_hw_port_quiesce_ctx *port_quiesce_ctx = NULL;
|
|
uint32_t i = 0;
|
|
int err = 0;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port:%d is being quiesced.\n", fc_port->port_hdl);
|
|
|
|
/*
|
|
* If the port is in an OFFLINE state, set the state to QUIESCED
|
|
* and execute the callback.
|
|
*/
|
|
if (fc_port->hw_port_status == SPDK_FC_PORT_OFFLINE) {
|
|
fc_port->hw_port_status = SPDK_FC_PORT_QUIESCED;
|
|
}
|
|
|
|
if (fc_port->hw_port_status == SPDK_FC_PORT_QUIESCED) {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "Port %d already in quiesced state.\n",
|
|
fc_port->port_hdl);
|
|
/*
|
|
* Execute the callback function directly.
|
|
*/
|
|
cb_func(ctx, err);
|
|
goto out;
|
|
}
|
|
|
|
port_quiesce_ctx = calloc(1, sizeof(struct spdk_nvmf_fc_adm_hw_port_quiesce_ctx));
|
|
|
|
if (port_quiesce_ctx == NULL) {
|
|
err = -ENOMEM;
|
|
SPDK_ERRLOG("Failed to allocate memory for LS queue quiesce ctx, port:%d\n",
|
|
fc_port->port_hdl);
|
|
goto out;
|
|
}
|
|
|
|
port_quiesce_ctx->quiesce_count = 0;
|
|
port_quiesce_ctx->ctx = ctx;
|
|
port_quiesce_ctx->cb_func = cb_func;
|
|
|
|
/*
|
|
* Quiesce the LS queue.
|
|
*/
|
|
err = nvmf_fc_adm_hw_queue_quiesce(&fc_port->ls_queue, port_quiesce_ctx,
|
|
nvmf_fc_adm_queue_quiesce_cb);
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("Failed to quiesce the LS queue.\n");
|
|
goto out;
|
|
}
|
|
port_quiesce_ctx->quiesce_count++;
|
|
|
|
/*
|
|
* Quiesce the IO queues.
|
|
*/
|
|
for (i = 0; i < fc_port->num_io_queues; i++) {
|
|
err = nvmf_fc_adm_hw_queue_quiesce(&fc_port->io_queues[i],
|
|
port_quiesce_ctx,
|
|
nvmf_fc_adm_queue_quiesce_cb);
|
|
if (err != 0) {
|
|
DEV_VERIFY(0);
|
|
SPDK_ERRLOG("Failed to quiesce the IO queue:%d.\n", fc_port->io_queues[i].hwqp_id);
|
|
}
|
|
port_quiesce_ctx->quiesce_count++;
|
|
}
|
|
|
|
out:
|
|
if (port_quiesce_ctx && err != 0) {
|
|
free(port_quiesce_ctx);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Initialize and add a HW port entry to the global
|
|
* HW port list.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_hw_port_init(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_hw_port_init_args *args = (struct spdk_nvmf_fc_hw_port_init_args *)
|
|
api_data->api_args;
|
|
int err = 0;
|
|
|
|
if (args->io_queue_cnt > spdk_env_get_core_count()) {
|
|
SPDK_ERRLOG("IO queues count greater than cores for %d.\n", args->port_handle);
|
|
err = EINVAL;
|
|
goto abort_port_init;
|
|
}
|
|
|
|
/*
|
|
* 1. Check for duplicate initialization.
|
|
*/
|
|
fc_port = nvmf_fc_port_lookup(args->port_handle);
|
|
if (fc_port != NULL) {
|
|
SPDK_ERRLOG("Duplicate port found %d.\n", args->port_handle);
|
|
goto abort_port_init;
|
|
}
|
|
|
|
/*
|
|
* 2. Get the memory to instantiate a fc port.
|
|
*/
|
|
fc_port = calloc(1, sizeof(struct spdk_nvmf_fc_port) +
|
|
(args->io_queue_cnt * sizeof(struct spdk_nvmf_fc_hwqp)));
|
|
if (fc_port == NULL) {
|
|
SPDK_ERRLOG("Failed to allocate memory for fc_port %d.\n", args->port_handle);
|
|
err = -ENOMEM;
|
|
goto abort_port_init;
|
|
}
|
|
|
|
/* assign the io_queues array */
|
|
fc_port->io_queues = (struct spdk_nvmf_fc_hwqp *)((uint8_t *)fc_port + sizeof(
|
|
struct spdk_nvmf_fc_port));
|
|
|
|
/*
|
|
* 3. Initialize the contents for the FC-port
|
|
*/
|
|
err = nvmf_fc_adm_hw_port_data_init(fc_port, args);
|
|
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("Data initialization failed for fc_port %d.\n", args->port_handle);
|
|
DEV_VERIFY(!"Data initialization failed for fc_port");
|
|
goto abort_port_init;
|
|
}
|
|
|
|
/*
|
|
* 4. Add this port to the global fc port list in the library.
|
|
*/
|
|
nvmf_fc_port_add(fc_port);
|
|
|
|
abort_port_init:
|
|
if (err && fc_port) {
|
|
free(fc_port);
|
|
}
|
|
if (api_data->cb_func != NULL) {
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_HW_PORT_INIT, args->cb_ctx, err);
|
|
}
|
|
|
|
free(arg);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d initialize done, rc = %d.\n",
|
|
args->port_handle, err);
|
|
}
|
|
|
|
/*
|
|
* Online a HW port.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_hw_port_online(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = NULL;
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_hw_port_online_args *args = (struct spdk_nvmf_fc_hw_port_online_args *)
|
|
api_data->api_args;
|
|
int i = 0;
|
|
int err = 0;
|
|
|
|
fc_port = nvmf_fc_port_lookup(args->port_handle);
|
|
if (fc_port) {
|
|
/* Set the port state to online */
|
|
err = nvmf_fc_port_set_online(fc_port);
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("Hw port %d online failed. err = %d\n", fc_port->port_hdl, err);
|
|
DEV_VERIFY(!"Hw port online failed");
|
|
goto out;
|
|
}
|
|
|
|
hwqp = &fc_port->ls_queue;
|
|
hwqp->context = NULL;
|
|
(void)nvmf_fc_hwqp_set_online(hwqp);
|
|
|
|
/* Cycle through all the io queues and setup a hwqp poller for each. */
|
|
for (i = 0; i < (int)fc_port->num_io_queues; i++) {
|
|
hwqp = &fc_port->io_queues[i];
|
|
hwqp->context = NULL;
|
|
(void)nvmf_fc_hwqp_set_online(hwqp);
|
|
nvmf_fc_poll_group_add_hwqp(hwqp);
|
|
}
|
|
} else {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC port %d\n", args->port_handle);
|
|
err = -EINVAL;
|
|
}
|
|
|
|
out:
|
|
if (api_data->cb_func != NULL) {
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_HW_PORT_ONLINE, args->cb_ctx, err);
|
|
}
|
|
|
|
free(arg);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d online done, rc = %d.\n", args->port_handle,
|
|
err);
|
|
}
|
|
|
|
/*
|
|
* Offline a HW port.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_hw_port_offline(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = NULL;
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_hw_port_offline_args *args = (struct spdk_nvmf_fc_hw_port_offline_args *)
|
|
api_data->api_args;
|
|
int i = 0;
|
|
int err = 0;
|
|
|
|
fc_port = nvmf_fc_port_lookup(args->port_handle);
|
|
if (fc_port) {
|
|
/* Set the port state to offline, if it is not already. */
|
|
err = nvmf_fc_port_set_offline(fc_port);
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("Hw port %d already offline. err = %d\n", fc_port->port_hdl, err);
|
|
err = 0;
|
|
goto out;
|
|
}
|
|
|
|
hwqp = &fc_port->ls_queue;
|
|
(void)nvmf_fc_hwqp_set_offline(hwqp);
|
|
|
|
/* Remove poller for all the io queues. */
|
|
for (i = 0; i < (int)fc_port->num_io_queues; i++) {
|
|
hwqp = &fc_port->io_queues[i];
|
|
(void)nvmf_fc_hwqp_set_offline(hwqp);
|
|
nvmf_fc_poll_group_remove_hwqp(hwqp);
|
|
}
|
|
|
|
/*
|
|
* Delete all the nports. Ideally, the nports should have been purged
|
|
* before the offline event, in which case, only a validation is required.
|
|
*/
|
|
nvmf_fc_adm_hw_port_offline_nport_delete(fc_port);
|
|
} else {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC port %d\n", args->port_handle);
|
|
err = -EINVAL;
|
|
}
|
|
out:
|
|
if (api_data->cb_func != NULL) {
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_HW_PORT_OFFLINE, args->cb_ctx, err);
|
|
}
|
|
|
|
free(arg);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d offline done, rc = %d.\n", args->port_handle,
|
|
err);
|
|
}
|
|
|
|
struct nvmf_fc_add_rem_listener_ctx {
|
|
struct spdk_nvmf_subsystem *subsystem;
|
|
bool add_listener;
|
|
struct spdk_nvme_transport_id trid;
|
|
};
|
|
|
|
static void
|
|
nvmf_fc_adm_subsystem_resume_cb(struct spdk_nvmf_subsystem *subsystem, void *cb_arg, int status)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct nvmf_fc_add_rem_listener_ctx *ctx = (struct nvmf_fc_add_rem_listener_ctx *)cb_arg;
|
|
free(ctx);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_adm_listen_done(void *cb_arg, int status)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct nvmf_fc_add_rem_listener_ctx *ctx = cb_arg;
|
|
|
|
if (spdk_nvmf_subsystem_resume(ctx->subsystem, nvmf_fc_adm_subsystem_resume_cb, ctx)) {
|
|
SPDK_ERRLOG("Failed to resume subsystem: %s\n", ctx->subsystem->subnqn);
|
|
free(ctx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_adm_subsystem_paused_cb(struct spdk_nvmf_subsystem *subsystem, void *cb_arg, int status)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct nvmf_fc_add_rem_listener_ctx *ctx = (struct nvmf_fc_add_rem_listener_ctx *)cb_arg;
|
|
|
|
if (ctx->add_listener) {
|
|
spdk_nvmf_subsystem_add_listener(subsystem, &ctx->trid, nvmf_fc_adm_listen_done, ctx);
|
|
} else {
|
|
spdk_nvmf_subsystem_remove_listener(subsystem, &ctx->trid);
|
|
nvmf_fc_adm_listen_done(ctx, 0);
|
|
}
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_adm_add_rem_nport_listener(struct spdk_nvmf_fc_nport *nport, bool add)
|
|
{
|
|
struct spdk_nvmf_tgt *tgt = nvmf_fc_get_tgt();
|
|
struct spdk_nvmf_subsystem *subsystem;
|
|
|
|
if (!tgt) {
|
|
SPDK_ERRLOG("No nvmf target defined\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
subsystem = spdk_nvmf_subsystem_get_first(tgt);
|
|
while (subsystem) {
|
|
struct nvmf_fc_add_rem_listener_ctx *ctx;
|
|
|
|
if (spdk_nvmf_subsytem_any_listener_allowed(subsystem) == true) {
|
|
ctx = calloc(1, sizeof(struct nvmf_fc_add_rem_listener_ctx));
|
|
if (ctx) {
|
|
ctx->add_listener = add;
|
|
ctx->subsystem = subsystem;
|
|
nvmf_fc_create_trid(&ctx->trid,
|
|
nport->fc_nodename.u.wwn,
|
|
nport->fc_portname.u.wwn);
|
|
|
|
if (spdk_nvmf_tgt_listen(subsystem->tgt, &ctx->trid)) {
|
|
SPDK_ERRLOG("Failed to add transport address %s to tgt listeners\n",
|
|
ctx->trid.traddr);
|
|
free(ctx);
|
|
} else if (spdk_nvmf_subsystem_pause(subsystem,
|
|
nvmf_fc_adm_subsystem_paused_cb,
|
|
ctx)) {
|
|
SPDK_ERRLOG("Failed to pause subsystem: %s\n",
|
|
subsystem->subnqn);
|
|
free(ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
subsystem = spdk_nvmf_subsystem_get_next(subsystem);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Create a Nport.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_nport_create(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_nport_create_args *args = (struct spdk_nvmf_fc_nport_create_args *)
|
|
api_data->api_args;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
int err = 0;
|
|
|
|
/*
|
|
* Get the physical port.
|
|
*/
|
|
fc_port = nvmf_fc_port_lookup(args->port_handle);
|
|
if (fc_port == NULL) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Check for duplicate initialization.
|
|
*/
|
|
nport = nvmf_fc_nport_find(args->port_handle, args->nport_handle);
|
|
if (nport != NULL) {
|
|
SPDK_ERRLOG("Duplicate SPDK FC nport %d exists for FC port:%d.\n", args->nport_handle,
|
|
args->port_handle);
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Get the memory to instantiate a fc nport.
|
|
*/
|
|
nport = calloc(1, sizeof(struct spdk_nvmf_fc_nport));
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Failed to allocate memory for nport %d.\n",
|
|
args->nport_handle);
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Initialize the contents for the nport
|
|
*/
|
|
nport->nport_hdl = args->nport_handle;
|
|
nport->port_hdl = args->port_handle;
|
|
nport->nport_state = SPDK_NVMF_FC_OBJECT_CREATED;
|
|
nport->fc_nodename = args->fc_nodename;
|
|
nport->fc_portname = args->fc_portname;
|
|
nport->d_id = args->d_id;
|
|
nport->fc_port = nvmf_fc_port_lookup(args->port_handle);
|
|
|
|
(void)nvmf_fc_nport_set_state(nport, SPDK_NVMF_FC_OBJECT_CREATED);
|
|
TAILQ_INIT(&nport->rem_port_list);
|
|
nport->rport_count = 0;
|
|
TAILQ_INIT(&nport->fc_associations);
|
|
nport->assoc_count = 0;
|
|
|
|
/*
|
|
* Populate the nport address (as listening address) to the nvmf subsystems.
|
|
*/
|
|
err = nvmf_fc_adm_add_rem_nport_listener(nport, true);
|
|
|
|
(void)nvmf_fc_port_add_nport(fc_port, nport);
|
|
out:
|
|
if (err && nport) {
|
|
free(nport);
|
|
}
|
|
|
|
if (api_data->cb_func != NULL) {
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_NPORT_CREATE, args->cb_ctx, err);
|
|
}
|
|
|
|
free(arg);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_adm_delete_nport_cb(uint8_t port_handle, enum spdk_fc_event event_type,
|
|
void *cb_args, int spdk_err)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_nport_del_cb_data *cb_data = cb_args;
|
|
struct spdk_nvmf_fc_nport *nport = cb_data->nport;
|
|
spdk_nvmf_fc_callback cb_func = cb_data->fc_cb_func;
|
|
int err = 0;
|
|
uint16_t nport_hdl = 0;
|
|
char log_str[256];
|
|
|
|
/*
|
|
* Assert on any delete failure.
|
|
*/
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Nport delete callback returned null nport");
|
|
DEV_VERIFY(!"nport is null.");
|
|
goto out;
|
|
}
|
|
|
|
nport_hdl = nport->nport_hdl;
|
|
if (0 != spdk_err) {
|
|
SPDK_ERRLOG("Nport delete callback returned error. FC Port: "
|
|
"%d, Nport: %d\n",
|
|
nport->port_hdl, nport->nport_hdl);
|
|
DEV_VERIFY(!"nport delete callback error.");
|
|
}
|
|
|
|
/*
|
|
* Free the nport if this is the last rport being deleted and
|
|
* execute the callback(s).
|
|
*/
|
|
if (nvmf_fc_nport_has_no_rport(nport)) {
|
|
if (0 != nport->assoc_count) {
|
|
SPDK_ERRLOG("association count != 0\n");
|
|
DEV_VERIFY(!"association count != 0");
|
|
}
|
|
|
|
err = nvmf_fc_port_remove_nport(nport->fc_port, nport);
|
|
if (0 != err) {
|
|
SPDK_ERRLOG("Nport delete callback: Failed to remove "
|
|
"nport from nport list. FC Port:%d Nport:%d\n",
|
|
nport->port_hdl, nport->nport_hdl);
|
|
}
|
|
/* Free the nport */
|
|
free(nport);
|
|
|
|
if (cb_func != NULL) {
|
|
(void)cb_func(cb_data->port_handle, SPDK_FC_NPORT_DELETE, cb_data->fc_cb_ctx, spdk_err);
|
|
}
|
|
free(cb_data);
|
|
}
|
|
out:
|
|
snprintf(log_str, sizeof(log_str),
|
|
"port:%d nport:%d delete cb exit, evt_type:%d rc:%d.\n",
|
|
port_handle, nport_hdl, event_type, spdk_err);
|
|
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("%s", log_str);
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "%s", log_str);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Delete Nport.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_nport_delete(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_nport_delete_args *args = (struct spdk_nvmf_fc_nport_delete_args *)
|
|
api_data->api_args;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
struct spdk_nvmf_fc_adm_nport_del_cb_data *cb_data = NULL;
|
|
struct spdk_nvmf_fc_remote_port_info *rport_iter = NULL;
|
|
int err = 0;
|
|
uint32_t rport_cnt = 0;
|
|
int rc = 0;
|
|
|
|
/*
|
|
* Make sure that the nport exists.
|
|
*/
|
|
nport = nvmf_fc_nport_find(args->port_handle, args->nport_handle);
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC nport %d for FC Port: %d.\n", args->nport_handle,
|
|
args->port_handle);
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Allocate memory for callback data.
|
|
*/
|
|
cb_data = calloc(1, sizeof(struct spdk_nvmf_fc_adm_nport_del_cb_data));
|
|
if (NULL == cb_data) {
|
|
SPDK_ERRLOG("Failed to allocate memory for cb_data %d.\n", args->nport_handle);
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
cb_data->nport = nport;
|
|
cb_data->port_handle = args->port_handle;
|
|
cb_data->fc_cb_func = api_data->cb_func;
|
|
cb_data->fc_cb_ctx = args->cb_ctx;
|
|
|
|
/*
|
|
* Begin nport tear down
|
|
*/
|
|
if (nport->nport_state == SPDK_NVMF_FC_OBJECT_CREATED) {
|
|
(void)nvmf_fc_nport_set_state(nport, SPDK_NVMF_FC_OBJECT_TO_BE_DELETED);
|
|
} else if (nport->nport_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) {
|
|
/*
|
|
* Deletion of this nport already in progress. Register callback
|
|
* and return.
|
|
*/
|
|
/* TODO: Register callback in callback vector. For now, set the error and return. */
|
|
err = -ENODEV;
|
|
goto out;
|
|
} else {
|
|
/* nport partially created/deleted */
|
|
DEV_VERIFY(nport->nport_state == SPDK_NVMF_FC_OBJECT_ZOMBIE);
|
|
DEV_VERIFY(0 != "Nport in zombie state");
|
|
err = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Remove this nport from listening addresses across subsystems
|
|
*/
|
|
rc = nvmf_fc_adm_add_rem_nport_listener(nport, false);
|
|
|
|
if (0 != rc) {
|
|
err = nvmf_fc_nport_set_state(nport, SPDK_NVMF_FC_OBJECT_ZOMBIE);
|
|
SPDK_ERRLOG("Unable to remove the listen addr in the subsystems for nport %d.\n",
|
|
nport->nport_hdl);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Delete all the remote ports (if any) for the nport
|
|
*/
|
|
/* TODO - Need to do this with a "first" and a "next" accessor function
|
|
* for completeness. Look at app-subsystem as examples.
|
|
*/
|
|
if (nvmf_fc_nport_has_no_rport(nport)) {
|
|
/* No rports to delete. Complete the nport deletion. */
|
|
nvmf_fc_adm_delete_nport_cb(nport->port_hdl, SPDK_FC_NPORT_DELETE, cb_data, 0);
|
|
goto out;
|
|
}
|
|
|
|
TAILQ_FOREACH(rport_iter, &nport->rem_port_list, link) {
|
|
struct spdk_nvmf_fc_hw_i_t_delete_args *it_del_args = calloc(
|
|
1, sizeof(struct spdk_nvmf_fc_hw_i_t_delete_args));
|
|
|
|
if (it_del_args == NULL) {
|
|
err = -ENOMEM;
|
|
SPDK_ERRLOG("SPDK_FC_IT_DELETE no mem to delete rport with rpi:%d s_id:%d.\n",
|
|
rport_iter->rpi, rport_iter->s_id);
|
|
DEV_VERIFY(!"SPDK_FC_IT_DELETE failed, cannot allocate memory");
|
|
goto out;
|
|
}
|
|
|
|
rport_cnt++;
|
|
it_del_args->port_handle = nport->port_hdl;
|
|
it_del_args->nport_handle = nport->nport_hdl;
|
|
it_del_args->cb_ctx = (void *)cb_data;
|
|
it_del_args->rpi = rport_iter->rpi;
|
|
it_del_args->s_id = rport_iter->s_id;
|
|
|
|
nvmf_fc_main_enqueue_event(SPDK_FC_IT_DELETE, (void *)it_del_args,
|
|
nvmf_fc_adm_delete_nport_cb);
|
|
}
|
|
|
|
out:
|
|
/* On failure, execute the callback function now */
|
|
if ((err != 0) || (rc != 0)) {
|
|
SPDK_ERRLOG("NPort %d delete failed, error:%d, fc port:%d, "
|
|
"rport_cnt:%d rc:%d.\n",
|
|
args->nport_handle, err, args->port_handle,
|
|
rport_cnt, rc);
|
|
if (cb_data) {
|
|
free(cb_data);
|
|
}
|
|
if (api_data->cb_func != NULL) {
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_NPORT_DELETE, args->cb_ctx, err);
|
|
}
|
|
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api,
|
|
"NPort %d delete done succesfully, fc port:%d. "
|
|
"rport_cnt:%d\n",
|
|
args->nport_handle, args->port_handle, rport_cnt);
|
|
}
|
|
|
|
free(arg);
|
|
}
|
|
|
|
/*
|
|
* Process an PRLI/IT add.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_i_t_add(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_hw_i_t_add_args *args = (struct spdk_nvmf_fc_hw_i_t_add_args *)
|
|
api_data->api_args;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
struct spdk_nvmf_fc_remote_port_info *rport_iter = NULL;
|
|
struct spdk_nvmf_fc_remote_port_info *rport = NULL;
|
|
int err = 0;
|
|
|
|
/*
|
|
* Make sure the nport port exists.
|
|
*/
|
|
nport = nvmf_fc_nport_find(args->port_handle, args->nport_handle);
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC nport %d\n", args->nport_handle);
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Check for duplicate i_t_add.
|
|
*/
|
|
TAILQ_FOREACH(rport_iter, &nport->rem_port_list, link) {
|
|
if ((rport_iter->s_id == args->s_id) && (rport_iter->rpi == args->rpi)) {
|
|
SPDK_ERRLOG("Duplicate rport found for FC nport %d: sid:%d rpi:%d\n",
|
|
args->nport_handle, rport_iter->s_id, rport_iter->rpi);
|
|
err = -EEXIST;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the memory to instantiate the remote port
|
|
*/
|
|
rport = calloc(1, sizeof(struct spdk_nvmf_fc_remote_port_info));
|
|
if (rport == NULL) {
|
|
SPDK_ERRLOG("Memory allocation for rem port failed.\n");
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Initialize the contents for the rport
|
|
*/
|
|
(void)nvmf_fc_rport_set_state(rport, SPDK_NVMF_FC_OBJECT_CREATED);
|
|
rport->s_id = args->s_id;
|
|
rport->rpi = args->rpi;
|
|
rport->fc_nodename = args->fc_nodename;
|
|
rport->fc_portname = args->fc_portname;
|
|
|
|
/*
|
|
* Add remote port to nport
|
|
*/
|
|
if (nvmf_fc_nport_add_rem_port(nport, rport) != 0) {
|
|
DEV_VERIFY(!"Error while adding rport to list");
|
|
};
|
|
|
|
/*
|
|
* TODO: Do we validate the initiators service parameters?
|
|
*/
|
|
|
|
/*
|
|
* Get the targets service parameters from the library
|
|
* to return back to the driver.
|
|
*/
|
|
args->target_prli_info = nvmf_fc_get_prli_service_params();
|
|
|
|
out:
|
|
if (api_data->cb_func != NULL) {
|
|
/*
|
|
* Passing pointer to the args struct as the first argument.
|
|
* The cb_func should handle this appropriately.
|
|
*/
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_IT_ADD, args->cb_ctx, err);
|
|
}
|
|
|
|
free(arg);
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api,
|
|
"IT add on nport %d done, rc = %d.\n",
|
|
args->nport_handle, err);
|
|
}
|
|
|
|
/**
|
|
* Process a IT delete.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_i_t_delete(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_hw_i_t_delete_args *args = (struct spdk_nvmf_fc_hw_i_t_delete_args *)
|
|
api_data->api_args;
|
|
int rc = 0;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
struct spdk_nvmf_fc_adm_i_t_del_cb_data *cb_data = NULL;
|
|
struct spdk_nvmf_fc_remote_port_info *rport_iter = NULL;
|
|
struct spdk_nvmf_fc_remote_port_info *rport = NULL;
|
|
uint32_t num_rport = 0;
|
|
char log_str[256];
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "IT delete on nport:%d begin.\n", args->nport_handle);
|
|
|
|
/*
|
|
* Make sure the nport port exists. If it does not, error out.
|
|
*/
|
|
nport = nvmf_fc_nport_find(args->port_handle, args->nport_handle);
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC nport:%d\n", args->nport_handle);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Find this ITN / rport (remote port).
|
|
*/
|
|
TAILQ_FOREACH(rport_iter, &nport->rem_port_list, link) {
|
|
num_rport++;
|
|
if ((rport_iter->s_id == args->s_id) &&
|
|
(rport_iter->rpi == args->rpi) &&
|
|
(rport_iter->rport_state == SPDK_NVMF_FC_OBJECT_CREATED)) {
|
|
rport = rport_iter;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We should find either zero or exactly one rport.
|
|
*
|
|
* If we find zero rports, that means that a previous request has
|
|
* removed the rport by the time we reached here. In this case,
|
|
* simply return out.
|
|
*/
|
|
if (rport == NULL) {
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* We have found exactly one rport. Allocate memory for callback data.
|
|
*/
|
|
cb_data = calloc(1, sizeof(struct spdk_nvmf_fc_adm_i_t_del_cb_data));
|
|
if (NULL == cb_data) {
|
|
SPDK_ERRLOG("Failed to allocate memory for cb_data for nport:%d.\n", args->nport_handle);
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
cb_data->nport = nport;
|
|
cb_data->rport = rport;
|
|
cb_data->port_handle = args->port_handle;
|
|
cb_data->fc_cb_func = api_data->cb_func;
|
|
cb_data->fc_cb_ctx = args->cb_ctx;
|
|
|
|
/*
|
|
* Validate rport object state.
|
|
*/
|
|
if (rport->rport_state == SPDK_NVMF_FC_OBJECT_CREATED) {
|
|
(void)nvmf_fc_rport_set_state(rport, SPDK_NVMF_FC_OBJECT_TO_BE_DELETED);
|
|
} else if (rport->rport_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) {
|
|
/*
|
|
* Deletion of this rport already in progress. Register callback
|
|
* and return.
|
|
*/
|
|
/* TODO: Register callback in callback vector. For now, set the error and return. */
|
|
rc = -ENODEV;
|
|
goto out;
|
|
} else {
|
|
/* rport partially created/deleted */
|
|
DEV_VERIFY(rport->rport_state == SPDK_NVMF_FC_OBJECT_ZOMBIE);
|
|
DEV_VERIFY(!"Invalid rport_state");
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* We have successfully found a rport to delete. Call
|
|
* nvmf_fc_i_t_delete_assoc(), which will perform further
|
|
* IT-delete processing as well as free the cb_data.
|
|
*/
|
|
nvmf_fc_adm_i_t_delete_assoc(nport, rport, nvmf_fc_adm_i_t_delete_cb,
|
|
(void *)cb_data);
|
|
|
|
out:
|
|
if (rc != 0) {
|
|
/*
|
|
* We have entered here because either we encountered an
|
|
* error, or we did not find a rport to delete.
|
|
* As a result, we will not call the function
|
|
* nvmf_fc_i_t_delete_assoc() for further IT-delete
|
|
* processing. Therefore, execute the callback function now.
|
|
*/
|
|
if (cb_data) {
|
|
free(cb_data);
|
|
}
|
|
if (api_data->cb_func != NULL) {
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_IT_DELETE, args->cb_ctx, rc);
|
|
}
|
|
}
|
|
|
|
snprintf(log_str, sizeof(log_str),
|
|
"IT delete on nport:%d end. num_rport:%d rc = %d.\n",
|
|
args->nport_handle, num_rport, rc);
|
|
|
|
if (rc != 0) {
|
|
SPDK_ERRLOG("%s", log_str);
|
|
} else {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "%s", log_str);
|
|
}
|
|
|
|
free(arg);
|
|
}
|
|
|
|
/*
|
|
* Process ABTS received
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_abts_recv(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_abts_args *args = (struct spdk_nvmf_fc_abts_args *)api_data->api_args;
|
|
struct spdk_nvmf_fc_nport *nport = NULL;
|
|
int err = 0;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "FC ABTS received. RPI:%d, oxid:%d, rxid:%d\n", args->rpi,
|
|
args->oxid, args->rxid);
|
|
|
|
/*
|
|
* 1. Make sure the nport port exists.
|
|
*/
|
|
nport = nvmf_fc_nport_find(args->port_handle, args->nport_handle);
|
|
if (nport == NULL) {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC nport %d\n", args->nport_handle);
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* 2. If the nport is in the process of being deleted, drop the ABTS.
|
|
*/
|
|
if (nport->nport_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) {
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api,
|
|
"FC ABTS dropped because the nport is being deleted; RPI:%d, oxid:%d, rxid:%d\n",
|
|
args->rpi, args->oxid, args->rxid);
|
|
err = 0;
|
|
goto out;
|
|
|
|
}
|
|
|
|
/*
|
|
* 3. Pass the received ABTS-LS to the library for handling.
|
|
*/
|
|
nvmf_fc_handle_abts_frame(nport, args->rpi, args->oxid, args->rxid);
|
|
|
|
out:
|
|
if (api_data->cb_func != NULL) {
|
|
/*
|
|
* Passing pointer to the args struct as the first argument.
|
|
* The cb_func should handle this appropriately.
|
|
*/
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_ABTS_RECV, args, err);
|
|
} else {
|
|
/* No callback set, free the args */
|
|
free(args);
|
|
}
|
|
|
|
free(arg);
|
|
}
|
|
|
|
/*
|
|
* Callback function for hw port quiesce.
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_hw_port_quiesce_reset_cb(void *ctx, int err)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_hw_port_reset_ctx *reset_ctx =
|
|
(struct spdk_nvmf_fc_adm_hw_port_reset_ctx *)ctx;
|
|
struct spdk_nvmf_fc_hw_port_reset_args *args = reset_ctx->reset_args;
|
|
spdk_nvmf_fc_callback cb_func = reset_ctx->reset_cb_func;
|
|
struct spdk_nvmf_fc_queue_dump_info dump_info;
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
char *dump_buf = NULL;
|
|
uint32_t dump_buf_size = SPDK_FC_HW_DUMP_BUF_SIZE;
|
|
|
|
/*
|
|
* Free the callback context struct.
|
|
*/
|
|
free(ctx);
|
|
|
|
if (err != 0) {
|
|
SPDK_ERRLOG("Port %d quiesce operation failed.\n", args->port_handle);
|
|
goto out;
|
|
}
|
|
|
|
if (args->dump_queues == false) {
|
|
/*
|
|
* Queues need not be dumped.
|
|
*/
|
|
goto out;
|
|
}
|
|
|
|
SPDK_ERRLOG("Dumping queues for HW port %d\n", args->port_handle);
|
|
|
|
/*
|
|
* Get the fc port.
|
|
*/
|
|
fc_port = nvmf_fc_port_lookup(args->port_handle);
|
|
if (fc_port == NULL) {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC port %d\n", args->port_handle);
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Allocate memory for the dump buffer.
|
|
* This memory will be freed by FCT.
|
|
*/
|
|
dump_buf = (char *)calloc(1, dump_buf_size);
|
|
if (dump_buf == NULL) {
|
|
err = -ENOMEM;
|
|
SPDK_ERRLOG("Memory allocation for dump buffer failed, SPDK FC port %d\n", args->port_handle);
|
|
goto out;
|
|
}
|
|
*args->dump_buf = (uint32_t *)dump_buf;
|
|
dump_info.buffer = dump_buf;
|
|
dump_info.offset = 0;
|
|
|
|
/*
|
|
* Add the dump reason to the top of the buffer.
|
|
*/
|
|
nvmf_fc_dump_buf_print(&dump_info, "%s\n", args->reason);
|
|
|
|
/*
|
|
* Dump the hwqp.
|
|
*/
|
|
nvmf_fc_dump_all_queues(&fc_port->ls_queue, fc_port->io_queues,
|
|
fc_port->num_io_queues, &dump_info);
|
|
|
|
out:
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d reset done, queues_dumped = %d, rc = %d.\n",
|
|
args->port_handle, args->dump_queues, err);
|
|
|
|
if (cb_func != NULL) {
|
|
(void)cb_func(args->port_handle, SPDK_FC_HW_PORT_RESET, args->cb_ctx, err);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* HW port reset
|
|
|
|
*/
|
|
static void
|
|
nvmf_fc_adm_evnt_hw_port_reset(void *arg)
|
|
{
|
|
ASSERT_SPDK_FC_MAIN_THREAD();
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = (struct spdk_nvmf_fc_adm_api_data *)arg;
|
|
struct spdk_nvmf_fc_hw_port_reset_args *args = (struct spdk_nvmf_fc_hw_port_reset_args *)
|
|
api_data->api_args;
|
|
struct spdk_nvmf_fc_port *fc_port = NULL;
|
|
struct spdk_nvmf_fc_adm_hw_port_reset_ctx *ctx = NULL;
|
|
int err = 0;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d dump\n", args->port_handle);
|
|
|
|
/*
|
|
* Make sure the physical port exists.
|
|
*/
|
|
fc_port = nvmf_fc_port_lookup(args->port_handle);
|
|
if (fc_port == NULL) {
|
|
SPDK_ERRLOG("Unable to find the SPDK FC port %d\n", args->port_handle);
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Save the reset event args and the callback in a context struct.
|
|
*/
|
|
ctx = calloc(1, sizeof(struct spdk_nvmf_fc_adm_hw_port_reset_ctx));
|
|
|
|
if (ctx == NULL) {
|
|
err = -ENOMEM;
|
|
SPDK_ERRLOG("Memory allocation for reset ctx failed, SPDK FC port %d\n", args->port_handle);
|
|
goto fail;
|
|
}
|
|
|
|
ctx->reset_args = arg;
|
|
ctx->reset_cb_func = api_data->cb_func;
|
|
|
|
/*
|
|
* Quiesce the hw port.
|
|
*/
|
|
err = nvmf_fc_adm_hw_port_quiesce(fc_port, ctx, nvmf_fc_adm_hw_port_quiesce_reset_cb);
|
|
if (err != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Once the ports are successfully quiesced the reset processing
|
|
* will continue in the callback function: spdk_fc_port_quiesce_reset_cb
|
|
*/
|
|
return;
|
|
fail:
|
|
free(ctx);
|
|
|
|
out:
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "HW port %d dump done, rc = %d.\n", args->port_handle,
|
|
err);
|
|
|
|
if (api_data->cb_func != NULL) {
|
|
(void)api_data->cb_func(args->port_handle, SPDK_FC_HW_PORT_RESET, args->cb_ctx, err);
|
|
}
|
|
|
|
free(arg);
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_adm_run_on_main_thread(spdk_msg_fn fn, void *args)
|
|
{
|
|
if (nvmf_fc_get_main_thread()) {
|
|
spdk_thread_send_msg(nvmf_fc_get_main_thread(), fn, args);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Queue up an event in the SPDK main threads event queue.
|
|
* Used by the FC driver to notify the SPDK main thread of FC related events.
|
|
*/
|
|
int
|
|
nvmf_fc_main_enqueue_event(enum spdk_fc_event event_type, void *args,
|
|
spdk_nvmf_fc_callback cb_func)
|
|
{
|
|
int err = 0;
|
|
struct spdk_nvmf_fc_adm_api_data *api_data = NULL;
|
|
spdk_msg_fn event_fn = NULL;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "Enqueue event %d.\n", event_type);
|
|
|
|
if (event_type >= SPDK_FC_EVENT_MAX) {
|
|
SPDK_ERRLOG("Invalid spdk_fc_event_t %d.\n", event_type);
|
|
err = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
if (args == NULL) {
|
|
SPDK_ERRLOG("Null args for event %d.\n", event_type);
|
|
err = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
api_data = calloc(1, sizeof(*api_data));
|
|
|
|
if (api_data == NULL) {
|
|
SPDK_ERRLOG("Failed to alloc api data for event %d.\n", event_type);
|
|
err = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
api_data->api_args = args;
|
|
api_data->cb_func = cb_func;
|
|
|
|
switch (event_type) {
|
|
case SPDK_FC_HW_PORT_INIT:
|
|
event_fn = nvmf_fc_adm_evnt_hw_port_init;
|
|
break;
|
|
|
|
case SPDK_FC_HW_PORT_ONLINE:
|
|
event_fn = nvmf_fc_adm_evnt_hw_port_online;
|
|
break;
|
|
|
|
case SPDK_FC_HW_PORT_OFFLINE:
|
|
event_fn = nvmf_fc_adm_evnt_hw_port_offline;
|
|
break;
|
|
|
|
case SPDK_FC_NPORT_CREATE:
|
|
event_fn = nvmf_fc_adm_evnt_nport_create;
|
|
break;
|
|
|
|
case SPDK_FC_NPORT_DELETE:
|
|
event_fn = nvmf_fc_adm_evnt_nport_delete;
|
|
break;
|
|
|
|
case SPDK_FC_IT_ADD:
|
|
event_fn = nvmf_fc_adm_evnt_i_t_add;
|
|
break;
|
|
|
|
case SPDK_FC_IT_DELETE:
|
|
event_fn = nvmf_fc_adm_evnt_i_t_delete;
|
|
break;
|
|
|
|
case SPDK_FC_ABTS_RECV:
|
|
event_fn = nvmf_fc_adm_evnt_abts_recv;
|
|
break;
|
|
|
|
case SPDK_FC_HW_PORT_RESET:
|
|
event_fn = nvmf_fc_adm_evnt_hw_port_reset;
|
|
break;
|
|
|
|
case SPDK_FC_UNRECOVERABLE_ERR:
|
|
default:
|
|
SPDK_ERRLOG("Invalid spdk_fc_event_t: %d\n", event_type);
|
|
err = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
|
|
if (err == 0) {
|
|
assert(event_fn != NULL);
|
|
nvmf_fc_adm_run_on_main_thread(event_fn, (void *)api_data);
|
|
SPDK_DEBUGLOG(nvmf_fc_adm_api, "Enqueue event %d done successfully\n", event_type);
|
|
} else {
|
|
SPDK_ERRLOG("Enqueue event %d failed, err = %d\n", event_type, err);
|
|
if (api_data) {
|
|
free(api_data);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
SPDK_NVMF_TRANSPORT_REGISTER(fc, &spdk_nvmf_transport_fc);
|
|
SPDK_LOG_REGISTER_COMPONENT(nvmf_fc_adm_api)
|
|
SPDK_LOG_REGISTER_COMPONENT(nvmf_fc)
|