ed56a3d482
- New files and updates to existing SPDK files to add the NVMf-FC transport. - Depends on an existing low level driver library. This driver is not part of SPDK repository. - Makefile updates to build FC transport (using CONFIG_FC) - Update configure script for FC build. - New FC unit test for FC-LS commands. - Update unittest.sh to run FC unit test (when built). Signed-off-by: John Barnard <john.barnard@broadcom.com> Signed-off-by: Anil Veerabhadrappa <anil.veerabhadrappa@broadcom.com> Change-Id: If31d4d25feab76c2dbe90a7faf71d465c2c3a354 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/450077 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com>
1682 lines
52 KiB
C
1682 lines
52 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.
|
|
*/
|
|
|
|
#include "spdk/env.h"
|
|
#include "spdk/assert.h"
|
|
#include "spdk/nvmf.h"
|
|
#include "spdk/nvmf_spec.h"
|
|
#include "spdk/string.h"
|
|
#include "spdk/trace.h"
|
|
#include "spdk/util.h"
|
|
#include "spdk/endian.h"
|
|
#include "spdk_internal/log.h"
|
|
#include "nvmf_internal.h"
|
|
#include "transport.h"
|
|
|
|
#include "nvmf_fc.h"
|
|
#include "fc_lld.h"
|
|
|
|
/* set to 1 to send ls disconnect in response to ls disconnect from host (per standard) */
|
|
#define NVMF_FC_LS_SEND_LS_DISCONNECT 0
|
|
|
|
/* Validation Error indexes into the string table below */
|
|
enum {
|
|
VERR_NO_ERROR = 0,
|
|
VERR_CR_ASSOC_LEN = 1,
|
|
VERR_CR_ASSOC_RQST_LEN = 2,
|
|
VERR_CR_ASSOC_CMD = 3,
|
|
VERR_CR_ASSOC_CMD_LEN = 4,
|
|
VERR_ERSP_RATIO = 5,
|
|
VERR_ASSOC_ALLOC_FAIL = 6,
|
|
VERR_CONN_ALLOC_FAIL = 7,
|
|
VERR_CR_CONN_LEN = 8,
|
|
VERR_CR_CONN_RQST_LEN = 9,
|
|
VERR_ASSOC_ID = 10,
|
|
VERR_ASSOC_ID_LEN = 11,
|
|
VERR_NO_ASSOC = 12,
|
|
VERR_CONN_ID = 13,
|
|
VERR_CONN_ID_LEN = 14,
|
|
VERR_NO_CONN = 15,
|
|
VERR_CR_CONN_CMD = 16,
|
|
VERR_CR_CONN_CMD_LEN = 17,
|
|
VERR_DISCONN_LEN = 18,
|
|
VERR_DISCONN_RQST_LEN = 19,
|
|
VERR_DISCONN_CMD = 20,
|
|
VERR_DISCONN_CMD_LEN = 21,
|
|
VERR_DISCONN_SCOPE = 22,
|
|
VERR_RS_LEN = 23,
|
|
VERR_RS_RQST_LEN = 24,
|
|
VERR_RS_CMD = 25,
|
|
VERR_RS_CMD_LEN = 26,
|
|
VERR_RS_RCTL = 27,
|
|
VERR_RS_RO = 28,
|
|
VERR_CONN_TOO_MANY = 29,
|
|
VERR_SUBNQN = 30,
|
|
VERR_HOSTNQN = 31,
|
|
VERR_SQSIZE = 32,
|
|
VERR_NO_RPORT = 33,
|
|
VERR_SUBLISTENER = 34,
|
|
};
|
|
|
|
static char *validation_errors[] = {
|
|
"OK",
|
|
"Bad CR_ASSOC Length",
|
|
"Bad CR_ASSOC Rqst Length",
|
|
"Not CR_ASSOC Cmd",
|
|
"Bad CR_ASSOC Cmd Length",
|
|
"Bad Ersp Ratio",
|
|
"Association Allocation Failed",
|
|
"Queue Allocation Failed",
|
|
"Bad CR_CONN Length",
|
|
"Bad CR_CONN Rqst Length",
|
|
"Not Association ID",
|
|
"Bad Association ID Length",
|
|
"No Association",
|
|
"Not Connection ID",
|
|
"Bad Connection ID Length",
|
|
"No Connection",
|
|
"Not CR_CONN Cmd",
|
|
"Bad CR_CONN Cmd Length",
|
|
"Bad DISCONN Length",
|
|
"Bad DISCONN Rqst Length",
|
|
"Not DISCONN Cmd",
|
|
"Bad DISCONN Cmd Length",
|
|
"Bad Disconnect Scope",
|
|
"Bad RS Length",
|
|
"Bad RS Rqst Length",
|
|
"Not RS Cmd",
|
|
"Bad RS Cmd Length",
|
|
"Bad RS R_CTL",
|
|
"Bad RS Relative Offset",
|
|
"Too many connections for association",
|
|
"Invalid subnqn or subsystem not found",
|
|
"Invalid hostnqn or subsystem doesn't allow host",
|
|
"SQ size = 0 or too big",
|
|
"No Remote Port",
|
|
"Bad Subsystem Port",
|
|
};
|
|
|
|
static inline void
|
|
nvmf_fc_add_assoc_to_tgt_port(struct spdk_nvmf_fc_nport *tgtport,
|
|
struct spdk_nvmf_fc_association *assoc,
|
|
struct spdk_nvmf_fc_remote_port_info *rport);
|
|
|
|
static inline FCNVME_BE32 cpu_to_be32(uint32_t in)
|
|
{
|
|
uint32_t t;
|
|
|
|
to_be32(&t, in);
|
|
return (FCNVME_BE32)t;
|
|
}
|
|
|
|
static inline FCNVME_BE32 nvmf_fc_lsdesc_len(size_t sz)
|
|
{
|
|
uint32_t t;
|
|
|
|
to_be32(&t, sz - (2 * sizeof(uint32_t)));
|
|
return (FCNVME_BE32)t;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_ls_format_rsp_hdr(void *buf, uint8_t ls_cmd, uint32_t desc_len,
|
|
uint8_t rqst_ls_cmd)
|
|
{
|
|
struct spdk_nvmf_fc_ls_acc_hdr *acc_hdr = buf;
|
|
|
|
acc_hdr->w0.ls_cmd = ls_cmd;
|
|
acc_hdr->desc_list_len = desc_len;
|
|
to_be32(&acc_hdr->rqst.desc_tag, FCNVME_LSDESC_RQST);
|
|
acc_hdr->rqst.desc_len =
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_rqst));
|
|
acc_hdr->rqst.w0.ls_cmd = rqst_ls_cmd;
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_ls_format_rjt(void *buf, uint16_t buflen, uint8_t ls_cmd,
|
|
uint8_t reason, uint8_t explanation, uint8_t vendor)
|
|
{
|
|
struct spdk_nvmf_fc_ls_rjt *rjt = buf;
|
|
|
|
bzero(buf, sizeof(struct spdk_nvmf_fc_ls_rjt));
|
|
nvmf_fc_ls_format_rsp_hdr(buf, FCNVME_LSDESC_RQST,
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_rjt)),
|
|
ls_cmd);
|
|
to_be32(&rjt->rjt.desc_tag, FCNVME_LSDESC_RJT);
|
|
rjt->rjt.desc_len = nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_rjt));
|
|
rjt->rjt.reason_code = reason;
|
|
rjt->rjt.reason_explanation = explanation;
|
|
rjt->rjt.vendor = vendor;
|
|
|
|
return sizeof(struct spdk_nvmf_fc_ls_rjt);
|
|
}
|
|
|
|
/* ************************************************** */
|
|
/* Allocators/Deallocators (assocations, connections, */
|
|
/* poller API data) */
|
|
|
|
static inline void
|
|
nvmf_fc_ls_free_association(struct spdk_nvmf_fc_association *assoc)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
/* return the q slots of the conns for the association */
|
|
TAILQ_FOREACH(fc_conn, &assoc->avail_fc_conns, assoc_avail_link) {
|
|
if (fc_conn->conn_id != NVMF_FC_INVALID_CONN_ID) {
|
|
nvmf_fc_release_conn(fc_conn->hwqp, fc_conn->conn_id,
|
|
fc_conn->max_queue_depth);
|
|
}
|
|
}
|
|
|
|
/* free assocation's send disconnect buffer */
|
|
if (assoc->snd_disconn_bufs) {
|
|
nvmf_fc_free_srsr_bufs(assoc->snd_disconn_bufs);
|
|
}
|
|
|
|
/* free assocation's connections */
|
|
free(assoc->conns_buf);
|
|
|
|
/* free the association */
|
|
free(assoc);
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_ls_alloc_connections(struct spdk_nvmf_fc_association *assoc,
|
|
struct spdk_nvmf_transport *nvmf_transport)
|
|
{
|
|
uint32_t i;
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Pre-alloc %d qpairs for host NQN %s\n",
|
|
nvmf_transport->opts.max_qpairs_per_ctrlr, assoc->host_nqn);
|
|
|
|
/* allocate memory for all connections at once */
|
|
assoc->conns_buf = calloc(nvmf_transport->opts.max_qpairs_per_ctrlr + 1,
|
|
sizeof(struct spdk_nvmf_fc_conn));
|
|
if (assoc->conns_buf == NULL) {
|
|
SPDK_ERRLOG("Out of memory for connections for new association\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < nvmf_transport->opts.max_qpairs_per_ctrlr; i++) {
|
|
fc_conn = assoc->conns_buf + (i * sizeof(struct spdk_nvmf_fc_conn));
|
|
fc_conn->conn_id = NVMF_FC_INVALID_CONN_ID;
|
|
fc_conn->qpair.state = SPDK_NVMF_QPAIR_UNINITIALIZED;
|
|
fc_conn->qpair.transport = nvmf_transport;
|
|
|
|
TAILQ_INSERT_TAIL(&assoc->avail_fc_conns, fc_conn, assoc_avail_link);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct spdk_nvmf_fc_association *
|
|
nvmf_fc_ls_new_association(uint32_t s_id,
|
|
struct spdk_nvmf_fc_nport *tgtport,
|
|
struct spdk_nvmf_fc_remote_port_info *rport,
|
|
struct spdk_nvmf_fc_lsdesc_cr_assoc_cmd *a_cmd,
|
|
struct spdk_nvmf_subsystem *subsys,
|
|
uint16_t rpi,
|
|
struct spdk_nvmf_transport *nvmf_transport)
|
|
{
|
|
struct spdk_nvmf_fc_association *assoc;
|
|
int rc;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"New Association request for port %d nport %d rpi 0x%x\n",
|
|
tgtport->fc_port->port_hdl, tgtport->nport_hdl, rpi);
|
|
|
|
assert(rport);
|
|
if (!rport) {
|
|
SPDK_ERRLOG("rport is null.\n");
|
|
return NULL;
|
|
}
|
|
|
|
assoc = calloc(1, sizeof(struct spdk_nvmf_fc_association));
|
|
if (!assoc) {
|
|
SPDK_ERRLOG("unable to allocate memory for new association\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* initialize association */
|
|
#if (NVMF_FC_LS_SEND_LS_DISCONNECT == 1)
|
|
/* allocate buffers to send LS disconnect command to host */
|
|
assoc->snd_disconn_bufs =
|
|
nvmf_fc_alloc_srsr_bufs(sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst),
|
|
sizeof(struct spdk_nvmf_fc_ls_rjt));
|
|
if (!assoc->snd_disconn_bufs) {
|
|
SPDK_ERRLOG("no dma memory for association's ls disconnect bufs\n");
|
|
free(assoc);
|
|
return NULL;
|
|
}
|
|
|
|
assoc->snd_disconn_bufs->rpi = rpi;
|
|
#endif
|
|
assoc->s_id = s_id;
|
|
assoc->tgtport = tgtport;
|
|
assoc->rport = rport;
|
|
assoc->subsystem = subsys;
|
|
assoc->assoc_state = SPDK_NVMF_FC_OBJECT_CREATED;
|
|
memcpy(assoc->host_id, a_cmd->hostid, FCNVME_ASSOC_HOSTID_LEN);
|
|
memcpy(assoc->host_nqn, a_cmd->hostnqn, FCNVME_ASSOC_HOSTNQN_LEN);
|
|
memcpy(assoc->sub_nqn, a_cmd->subnqn, FCNVME_ASSOC_HOSTNQN_LEN);
|
|
TAILQ_INIT(&assoc->fc_conns);
|
|
TAILQ_INIT(&assoc->avail_fc_conns);
|
|
assoc->ls_del_op_ctx = NULL;
|
|
|
|
/* allocate and assign connections for association */
|
|
rc = nvmf_fc_ls_alloc_connections(assoc, nvmf_transport);
|
|
if (rc != 0) {
|
|
nvmf_fc_ls_free_association(assoc);
|
|
return NULL;
|
|
}
|
|
|
|
/* add association to target port's association list */
|
|
nvmf_fc_add_assoc_to_tgt_port(tgtport, assoc, rport);
|
|
return assoc;
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_ls_append_del_cb_ctx(struct spdk_nvmf_fc_association *assoc,
|
|
struct nvmf_fc_ls_op_ctx *opd)
|
|
{
|
|
/* append to delete assoc callback list */
|
|
if (!assoc->ls_del_op_ctx) {
|
|
assoc->ls_del_op_ctx = (void *)opd;
|
|
} else {
|
|
struct nvmf_fc_ls_op_ctx *nxt =
|
|
(struct nvmf_fc_ls_op_ctx *) assoc->ls_del_op_ctx;
|
|
while (nxt->next_op_ctx) {
|
|
nxt = nxt->next_op_ctx;
|
|
}
|
|
nxt->next_op_ctx = opd;
|
|
}
|
|
}
|
|
|
|
static struct spdk_nvmf_fc_conn *
|
|
nvmf_fc_ls_new_connection(struct spdk_nvmf_fc_association *assoc, uint16_t qid,
|
|
uint16_t esrp_ratio, uint16_t rpi, uint16_t sq_size,
|
|
struct spdk_nvmf_fc_nport *tgtport)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
fc_conn = TAILQ_FIRST(&assoc->avail_fc_conns);
|
|
if (!fc_conn) {
|
|
SPDK_ERRLOG("out of connections for association %p\n", assoc);
|
|
return NULL;
|
|
}
|
|
|
|
/* Remove from avail list and add to in use. */
|
|
TAILQ_REMOVE(&assoc->avail_fc_conns, fc_conn, assoc_avail_link);
|
|
TAILQ_INSERT_TAIL(&assoc->fc_conns, fc_conn, assoc_link);
|
|
|
|
if (qid == 0) {
|
|
/* AdminQ connection. */
|
|
assoc->aq_conn = fc_conn;
|
|
}
|
|
|
|
fc_conn->qpair.qid = qid;
|
|
fc_conn->qpair.sq_head_max = sq_size;
|
|
TAILQ_INIT(&fc_conn->qpair.outstanding);
|
|
fc_conn->esrp_ratio = esrp_ratio;
|
|
fc_conn->fc_assoc = assoc;
|
|
fc_conn->rpi = rpi;
|
|
fc_conn->max_queue_depth = sq_size + 1;
|
|
|
|
/* save target port trid in connection (for subsystem
|
|
* listener validation in fabric connect command)
|
|
*/
|
|
spdk_nvmf_fc_create_trid(&fc_conn->trid, tgtport->fc_nodename.u.wwn,
|
|
tgtport->fc_portname.u.wwn);
|
|
TAILQ_INIT(&fc_conn->pending_queue);
|
|
|
|
return fc_conn;
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_ls_free_connection(struct spdk_nvmf_fc_conn *fc_conn)
|
|
{
|
|
TAILQ_INSERT_TAIL(&fc_conn->fc_assoc->avail_fc_conns, fc_conn, assoc_avail_link);
|
|
}
|
|
|
|
/* End - Allocators/Deallocators (assocations, connections, */
|
|
/* poller API data) */
|
|
/* ******************************************************** */
|
|
|
|
static inline struct spdk_nvmf_fc_association *
|
|
nvmf_fc_ls_find_assoc(struct spdk_nvmf_fc_nport *tgtport, uint64_t assoc_id)
|
|
{
|
|
struct spdk_nvmf_fc_association *assoc = NULL;
|
|
|
|
TAILQ_FOREACH(assoc, &tgtport->fc_associations, link) {
|
|
if (assoc->assoc_id == assoc_id) {
|
|
if (assoc->assoc_state == SPDK_NVMF_FC_OBJECT_ZOMBIE) {
|
|
assoc = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return assoc;
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_add_assoc_to_tgt_port(struct spdk_nvmf_fc_nport *tgtport,
|
|
struct spdk_nvmf_fc_association *assoc,
|
|
struct spdk_nvmf_fc_remote_port_info *rport)
|
|
{
|
|
TAILQ_INSERT_TAIL(&tgtport->fc_associations, assoc, link);
|
|
tgtport->assoc_count++;
|
|
rport->assoc_count++;
|
|
}
|
|
|
|
static inline void
|
|
nvmf_fc_del_assoc_from_tgt_port(struct spdk_nvmf_fc_association *assoc)
|
|
{
|
|
struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport;
|
|
|
|
TAILQ_REMOVE(&tgtport->fc_associations, assoc, link);
|
|
tgtport->assoc_count--;
|
|
assoc->rport->assoc_count--;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_ls_rsp_fail_del_conn_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *opd =
|
|
(struct nvmf_fc_ls_op_ctx *)cb_data;
|
|
struct spdk_nvmf_fc_ls_del_conn_api_data *dp = &opd->u.del_conn;
|
|
struct spdk_nvmf_fc_association *assoc = dp->assoc;
|
|
struct spdk_nvmf_fc_conn *fc_conn = dp->args.fc_conn;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Delete Connection callback "
|
|
"for assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id,
|
|
fc_conn->conn_id);
|
|
|
|
if (dp->aq_conn) {
|
|
/* delete association */
|
|
nvmf_fc_del_assoc_from_tgt_port(assoc);
|
|
nvmf_fc_ls_free_association(assoc);
|
|
} else {
|
|
/* remove connection from association's connection list */
|
|
TAILQ_REMOVE(&assoc->fc_conns, fc_conn, assoc_link);
|
|
nvmf_fc_ls_free_connection(fc_conn);
|
|
}
|
|
|
|
free(opd);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_handle_xmt_ls_rsp_failure(struct spdk_nvmf_fc_association *assoc,
|
|
struct spdk_nvmf_fc_conn *fc_conn,
|
|
bool aq_conn)
|
|
{
|
|
struct spdk_nvmf_fc_ls_del_conn_api_data *api_data;
|
|
struct nvmf_fc_ls_op_ctx *opd = NULL;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Transmit LS response failure "
|
|
"for assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id,
|
|
fc_conn->conn_id);
|
|
|
|
|
|
/* create context for delete connection API */
|
|
opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
|
|
if (!opd) { /* hopefully this doesn't happen - if so, we leak the connection */
|
|
SPDK_ERRLOG("Mem alloc failed for del conn op data");
|
|
return;
|
|
}
|
|
|
|
api_data = &opd->u.del_conn;
|
|
api_data->assoc = assoc;
|
|
api_data->ls_rqst = NULL;
|
|
api_data->aq_conn = aq_conn;
|
|
api_data->args.fc_conn = fc_conn;
|
|
api_data->args.send_abts = false;
|
|
api_data->args.hwqp = fc_conn->hwqp;
|
|
api_data->args.cb_info.cb_thread = spdk_get_thread();
|
|
api_data->args.cb_info.cb_func = nvmf_fc_ls_rsp_fail_del_conn_cb;
|
|
api_data->args.cb_info.cb_data = opd;
|
|
|
|
spdk_nvmf_fc_poller_api_func(api_data->args.hwqp,
|
|
SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION,
|
|
&api_data->args);
|
|
}
|
|
|
|
/* callback from poller's ADD_Connection event */
|
|
static void
|
|
nvmf_fc_ls_add_conn_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *opd =
|
|
(struct nvmf_fc_ls_op_ctx *)cb_data;
|
|
struct spdk_nvmf_fc_ls_add_conn_api_data *dp = &opd->u.add_conn;
|
|
struct spdk_nvmf_fc_association *assoc = dp->assoc;
|
|
struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport;
|
|
struct spdk_nvmf_fc_conn *fc_conn = dp->args.fc_conn;
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst = dp->ls_rqst;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"add_conn_cb: assoc_id = 0x%lx, conn_id = 0x%lx\n",
|
|
assoc->assoc_id, fc_conn->conn_id);
|
|
|
|
fc_conn->create_opd = NULL;
|
|
|
|
if (assoc->assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) {
|
|
/* association is already being deleted - don't continue */
|
|
free(opd);
|
|
return;
|
|
}
|
|
|
|
if (dp->aq_conn) {
|
|
struct spdk_nvmf_fc_ls_cr_assoc_acc *assoc_acc =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
|
|
/* put connection and association ID in response */
|
|
to_be64(&assoc_acc->conn_id.connection_id, fc_conn->conn_id);
|
|
assoc_acc->assoc_id.association_id = assoc_acc->conn_id.connection_id;
|
|
} else {
|
|
struct spdk_nvmf_fc_ls_cr_conn_acc *conn_acc =
|
|
(struct spdk_nvmf_fc_ls_cr_conn_acc *)ls_rqst->rspbuf.virt;
|
|
/* put connection ID in response */
|
|
to_be64(&conn_acc->conn_id.connection_id, fc_conn->conn_id);
|
|
}
|
|
|
|
/* send LS response */
|
|
if (nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst) != 0) {
|
|
SPDK_ERRLOG("Send LS response for %s failed - cleaning up\n",
|
|
dp->aq_conn ? "association" : "connection");
|
|
nvmf_fc_handle_xmt_ls_rsp_failure(assoc, fc_conn,
|
|
dp->aq_conn);
|
|
} else {
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"LS response (conn_id 0x%lx) sent\n", fc_conn->conn_id);
|
|
}
|
|
|
|
free(opd);
|
|
}
|
|
|
|
void
|
|
nvmf_fc_ls_add_conn_failure(
|
|
struct spdk_nvmf_fc_association *assoc,
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst,
|
|
struct spdk_nvmf_fc_conn *fc_conn,
|
|
bool aq_conn)
|
|
{
|
|
struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst;
|
|
struct spdk_nvmf_fc_ls_cr_assoc_acc *acc;
|
|
struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport;
|
|
|
|
if (fc_conn->create_opd) {
|
|
free(fc_conn->create_opd);
|
|
fc_conn->create_opd = NULL;
|
|
}
|
|
|
|
rqst = (struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt;
|
|
acc = (struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
|
|
|
|
/* send failure response */
|
|
ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc,
|
|
FCNVME_MAX_LS_BUFFER_SIZE, rqst->w0.ls_cmd,
|
|
FCNVME_RJT_RC_INSUFF_RES,
|
|
FCNVME_RJT_EXP_NONE, 0);
|
|
|
|
nvmf_fc_ls_free_connection(fc_conn);
|
|
if (aq_conn) {
|
|
nvmf_fc_del_assoc_from_tgt_port(assoc);
|
|
nvmf_fc_ls_free_association(assoc);
|
|
}
|
|
|
|
nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
|
|
}
|
|
|
|
|
|
static void
|
|
nvmf_fc_ls_add_conn_to_poller(
|
|
struct spdk_nvmf_fc_association *assoc,
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst,
|
|
struct spdk_nvmf_fc_conn *fc_conn,
|
|
bool aq_conn)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *opd;
|
|
struct spdk_nvmf_fc_ls_add_conn_api_data *api_data;
|
|
struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport;
|
|
struct spdk_nvmf_fc_port *fc_port = tgtport->fc_port;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Add Connection to poller for "
|
|
"assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id,
|
|
fc_conn->conn_id);
|
|
|
|
opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
|
|
if (!opd) {
|
|
SPDK_ERRLOG("allocate api data for add conn op failed\n");
|
|
nvmf_fc_ls_add_conn_failure(assoc, ls_rqst, fc_conn, aq_conn);
|
|
return;
|
|
}
|
|
|
|
/* insert conn in association's connection list */
|
|
api_data = &opd->u.add_conn;
|
|
assoc->conn_count++;
|
|
|
|
api_data->args.fc_conn = fc_conn;
|
|
api_data->args.cb_info.cb_thread = spdk_get_thread();
|
|
api_data->args.cb_info.cb_func = nvmf_fc_ls_add_conn_cb;
|
|
api_data->args.cb_info.cb_data = (void *)opd;
|
|
api_data->assoc = assoc;
|
|
api_data->ls_rqst = ls_rqst;
|
|
api_data->aq_conn = aq_conn;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"New QP callback called.\n");
|
|
|
|
/* Let the nvmf_tgt decide which pollgroup to use. */
|
|
fc_conn->create_opd = opd;
|
|
fc_port->new_qp_cb(&fc_conn->qpair);
|
|
}
|
|
|
|
/* Delete association functions */
|
|
|
|
static void
|
|
nvmf_fc_do_del_assoc_cbs(struct nvmf_fc_ls_op_ctx *opd, int ret)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *nxt;
|
|
struct spdk_nvmf_fc_delete_assoc_api_data *dp;
|
|
|
|
while (opd) {
|
|
dp = &opd->u.del_assoc;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "performing delete assoc. callback\n");
|
|
dp->del_assoc_cb(dp->del_assoc_cb_data, ret);
|
|
|
|
nxt = opd->next_op_ctx;
|
|
free(opd);
|
|
opd = nxt;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fs_send_ls_disconnect_cb(void *hwqp, int32_t status, void *args)
|
|
{
|
|
if (args) {
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "free disconnect buffers\n");
|
|
nvmf_fc_free_srsr_bufs((struct spdk_nvmf_fc_srsr_bufs *)args);
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_del_all_conns_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data;
|
|
struct spdk_nvmf_fc_delete_assoc_api_data *dp = &opd->u.del_assoc;
|
|
struct spdk_nvmf_fc_association *assoc = dp->assoc;
|
|
struct spdk_nvmf_fc_conn *fc_conn = dp->args.fc_conn;
|
|
|
|
/* Assumption here is that there will be no error (i.e. ret=success).
|
|
* Since connections are deleted in parallel, nothing can be
|
|
* done anyway if there is an error because we need to complete
|
|
* all connection deletes and callback to caller */
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"Delete all connections for assoc_id 0x%lx, conn_id = %lx\n",
|
|
assoc->assoc_id, fc_conn->conn_id);
|
|
|
|
/* remove connection from association's connection list */
|
|
TAILQ_REMOVE(&assoc->fc_conns, fc_conn, assoc_link);
|
|
nvmf_fc_ls_free_connection(fc_conn);
|
|
|
|
if (--assoc->conn_count == 0) {
|
|
/* last connection - remove association from target port's association list */
|
|
struct nvmf_fc_ls_op_ctx *cb_opd = (struct nvmf_fc_ls_op_ctx *)assoc->ls_del_op_ctx;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"remove assoc. %lx\n", assoc->assoc_id);
|
|
nvmf_fc_del_assoc_from_tgt_port(assoc);
|
|
|
|
if (assoc->snd_disconn_bufs &&
|
|
assoc->tgtport->fc_port->hw_port_status == SPDK_FC_PORT_ONLINE) {
|
|
|
|
struct spdk_nvmf_fc_ls_disconnect_rqst *dc_rqst;
|
|
struct spdk_nvmf_fc_srsr_bufs *srsr_bufs;
|
|
|
|
dc_rqst = (struct spdk_nvmf_fc_ls_disconnect_rqst *)
|
|
assoc->snd_disconn_bufs->rqst;
|
|
|
|
bzero(dc_rqst, sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst));
|
|
|
|
/* fill in request descriptor */
|
|
dc_rqst->w0.ls_cmd = FCNVME_LS_DISCONNECT;
|
|
to_be32(&dc_rqst->desc_list_len,
|
|
sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst) -
|
|
(2 * sizeof(uint32_t)));
|
|
|
|
/* fill in disconnect command descriptor */
|
|
to_be32(&dc_rqst->disconn_cmd.desc_tag, FCNVME_LSDESC_DISCONN_CMD);
|
|
to_be32(&dc_rqst->disconn_cmd.desc_len,
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_disconn_cmd) -
|
|
(2 * sizeof(uint32_t)));
|
|
|
|
/* fill in association id descriptor */
|
|
to_be32(&dc_rqst->assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID),
|
|
to_be32(&dc_rqst->assoc_id.desc_len,
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id) -
|
|
(2 * sizeof(uint32_t)));
|
|
to_be64(&dc_rqst->assoc_id.association_id, assoc->assoc_id);
|
|
|
|
srsr_bufs = assoc->snd_disconn_bufs;
|
|
assoc->snd_disconn_bufs = NULL;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Send LS disconnect\n");
|
|
if (spdk_nvmf_fc_xmt_srsr_req(&assoc->tgtport->fc_port->ls_queue,
|
|
srsr_bufs, nvmf_fs_send_ls_disconnect_cb,
|
|
(void *)srsr_bufs)) {
|
|
SPDK_ERRLOG("Error sending LS disconnect\n");
|
|
assoc->snd_disconn_bufs = srsr_bufs;
|
|
}
|
|
}
|
|
|
|
nvmf_fc_ls_free_association(assoc);
|
|
|
|
/* perform callbacks to all callers to delete association */
|
|
nvmf_fc_do_del_assoc_cbs(cb_opd, 0);
|
|
|
|
}
|
|
|
|
free(opd);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_kill_io_del_all_conns_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Callback after killing outstanding ABTS.");
|
|
/*
|
|
* NOTE: We should not access any connection or association related data
|
|
* structures here.
|
|
*/
|
|
free(opd);
|
|
}
|
|
|
|
|
|
/* Disconnect/delete (association) request functions */
|
|
|
|
static int
|
|
nvmf_fc_delete_association(struct spdk_nvmf_fc_nport *tgtport,
|
|
uint64_t assoc_id, bool send_abts, bool backend_initiated,
|
|
spdk_nvmf_fc_del_assoc_cb del_assoc_cb,
|
|
void *cb_data, bool from_ls_rqst)
|
|
{
|
|
|
|
struct nvmf_fc_ls_op_ctx *opd, *opd_tail, *opd_head = NULL;
|
|
struct spdk_nvmf_fc_delete_assoc_api_data *api_data;
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
struct spdk_nvmf_fc_association *assoc =
|
|
nvmf_fc_ls_find_assoc(tgtport, assoc_id);
|
|
struct spdk_nvmf_fc_port *fc_port = tgtport->fc_port;
|
|
enum spdk_nvmf_fc_object_state assoc_state;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Delete association, "
|
|
"assoc_id 0x%lx\n", assoc_id);
|
|
|
|
if (!assoc) {
|
|
SPDK_ERRLOG("Delete association failed: %s\n",
|
|
validation_errors[VERR_NO_ASSOC]);
|
|
return VERR_NO_ASSOC;
|
|
}
|
|
|
|
/* create cb context to put in association's list of
|
|
* callbacks to call when delete association is done */
|
|
opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
|
|
if (!opd) {
|
|
SPDK_ERRLOG("Mem alloc failed for del assoc cb data");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
api_data = &opd->u.del_assoc;
|
|
api_data->assoc = assoc;
|
|
api_data->from_ls_rqst = from_ls_rqst;
|
|
api_data->del_assoc_cb = del_assoc_cb;
|
|
api_data->del_assoc_cb_data = cb_data;
|
|
api_data->args.cb_info.cb_data = opd;
|
|
nvmf_fc_ls_append_del_cb_ctx(assoc, opd);
|
|
|
|
assoc_state = assoc->assoc_state;
|
|
if ((assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) &&
|
|
(fc_port->hw_port_status != SPDK_FC_PORT_QUIESCED)) {
|
|
/* association already being deleted */
|
|
return 0;
|
|
}
|
|
|
|
/* mark assoc. to be deleted */
|
|
assoc->assoc_state = SPDK_NVMF_FC_OBJECT_TO_BE_DELETED;
|
|
|
|
/* create a list of all connection to delete */
|
|
TAILQ_FOREACH(fc_conn, &assoc->fc_conns, assoc_link) {
|
|
opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
|
|
if (!opd) { /* hopefully this doesn't happen */
|
|
SPDK_ERRLOG("Mem alloc failed for del conn op data");
|
|
while (opd_head) { /* free any contexts already allocated */
|
|
opd = opd_head;
|
|
opd_head = opd->next_op_ctx;
|
|
free(opd);
|
|
}
|
|
return -ENOMEM;
|
|
}
|
|
|
|
api_data = &opd->u.del_assoc;
|
|
api_data->args.fc_conn = fc_conn;
|
|
api_data->assoc = assoc;
|
|
api_data->args.send_abts = send_abts;
|
|
api_data->args.backend_initiated = backend_initiated;
|
|
api_data->args.hwqp = nvmf_fc_get_hwqp_from_conn_id(
|
|
assoc->tgtport->fc_port->io_queues,
|
|
assoc->tgtport->fc_port->num_io_queues,
|
|
fc_conn->conn_id);
|
|
api_data->args.cb_info.cb_thread = spdk_get_thread();
|
|
if ((fc_port->hw_port_status == SPDK_FC_PORT_QUIESCED) &&
|
|
(assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED)) {
|
|
/*
|
|
* If there are any connections deletes or IO abts that are
|
|
* stuck because of firmware reset, a second invocation of
|
|
* SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION will result in
|
|
* outstanding connections & requests being killed and
|
|
* their corresponding callbacks being executed.
|
|
*/
|
|
api_data->args.cb_info.cb_func = nvmf_fc_kill_io_del_all_conns_cb;
|
|
} else {
|
|
api_data->args.cb_info.cb_func = nvmf_fc_del_all_conns_cb;
|
|
}
|
|
api_data->args.cb_info.cb_data = opd;
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"conn_id = %lx\n", fc_conn->conn_id);
|
|
|
|
if (!opd_head) {
|
|
opd_head = opd;
|
|
} else {
|
|
opd_tail->next_op_ctx = opd;
|
|
}
|
|
opd_tail = opd;
|
|
}
|
|
|
|
/* make poller api calls to delete connetions */
|
|
while (opd_head) {
|
|
opd = opd_head;
|
|
opd_head = opd->next_op_ctx;
|
|
api_data = &opd->u.del_assoc;
|
|
spdk_nvmf_fc_poller_api_func(api_data->args.hwqp,
|
|
SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION,
|
|
&api_data->args);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_ls_disconnect_assoc_cb(void *cb_data, uint32_t err)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data;
|
|
struct spdk_nvmf_fc_ls_disconn_assoc_api_data *dp = &opd->u.disconn_assoc;
|
|
struct spdk_nvmf_fc_nport *tgtport = dp->tgtport;
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst = dp->ls_rqst;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Disconnect association callback begin "
|
|
"nport %d\n", tgtport->nport_hdl);
|
|
if (err != 0) {
|
|
/* send failure response */
|
|
struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt;
|
|
struct spdk_nvmf_fc_ls_cr_assoc_acc *acc =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
|
|
ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc,
|
|
FCNVME_MAX_LS_BUFFER_SIZE,
|
|
rqst->w0.ls_cmd,
|
|
FCNVME_RJT_RC_UNAB,
|
|
FCNVME_RJT_EXP_NONE,
|
|
0);
|
|
}
|
|
|
|
nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
|
|
|
|
free(opd);
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Disconnect association callback complete "
|
|
"nport %d err %d\n", tgtport->nport_hdl, err);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_ls_disconnect_assoc(struct spdk_nvmf_fc_nport *tgtport,
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst, uint64_t assoc_id)
|
|
{
|
|
struct nvmf_fc_ls_op_ctx *opd;
|
|
struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt;
|
|
struct spdk_nvmf_fc_ls_cr_assoc_acc *acc =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
|
|
struct spdk_nvmf_fc_ls_disconn_assoc_api_data *api_data;
|
|
int ret;
|
|
uint8_t reason = 0;
|
|
|
|
opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
|
|
if (!opd) {
|
|
/* send failure response */
|
|
SPDK_ERRLOG("Allocate disconn assoc op data failed\n");
|
|
reason = FCNVME_RJT_RC_INSUFF_RES;
|
|
goto send_rjt;
|
|
}
|
|
|
|
api_data = &opd->u.disconn_assoc;
|
|
api_data->tgtport = tgtport;
|
|
api_data->ls_rqst = ls_rqst;
|
|
ret = nvmf_fc_delete_association(tgtport, assoc_id,
|
|
false, false,
|
|
nvmf_fc_ls_disconnect_assoc_cb,
|
|
api_data, true);
|
|
if (!ret) {
|
|
return;
|
|
}
|
|
|
|
/* delete association failed */
|
|
switch (ret) {
|
|
case VERR_NO_ASSOC:
|
|
reason = FCNVME_RJT_RC_INV_ASSOC;
|
|
break;
|
|
case -ENOMEM:
|
|
reason = FCNVME_RJT_RC_INSUFF_RES;
|
|
break;
|
|
default:
|
|
reason = FCNVME_RJT_RC_LOGIC;
|
|
}
|
|
|
|
free(opd);
|
|
|
|
send_rjt:
|
|
ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc,
|
|
FCNVME_MAX_LS_BUFFER_SIZE,
|
|
rqst->w0.ls_cmd, reason,
|
|
FCNVME_RJT_EXP_NONE, 0);
|
|
nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
|
|
}
|
|
|
|
static int
|
|
nvmf_fc_ls_validate_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn)
|
|
{
|
|
|
|
if (!spdk_nvmf_subsystem_host_allowed(subsystem, hostnqn)) {
|
|
return -EPERM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* **************************** */
|
|
/* LS Reqeust Handler Functions */
|
|
|
|
static void
|
|
nvmf_fc_ls_process_cass(uint32_t s_id,
|
|
struct spdk_nvmf_fc_nport *tgtport,
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt;
|
|
struct spdk_nvmf_fc_ls_cr_assoc_acc *acc =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
|
|
struct spdk_nvmf_fc_association *assoc;
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
struct spdk_nvmf_subsystem *subsystem = NULL;
|
|
const char *hostnqn = (const char *)rqst->assoc_cmd.hostnqn;
|
|
int errmsg_ind = 0;
|
|
uint8_t rc = FCNVME_RJT_RC_NONE;
|
|
uint8_t ec = FCNVME_RJT_EXP_NONE;
|
|
struct spdk_nvmf_transport *transport = spdk_nvmf_tgt_get_transport(ls_rqst->nvmf_tgt,
|
|
(enum spdk_nvme_transport_type) SPDK_NVMF_TRTYPE_FC);
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"LS_CASS: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d, sq_size=%d, "
|
|
"Subnqn: %s, Hostnqn: %s, Tgtport nn:%lx, pn:%lx\n",
|
|
ls_rqst->rqst_len, from_be32(&rqst->desc_list_len),
|
|
from_be32(&rqst->assoc_cmd.desc_len),
|
|
from_be32(&rqst->assoc_cmd.sqsize),
|
|
rqst->assoc_cmd.subnqn, hostnqn,
|
|
tgtport->fc_nodename.u.wwn, tgtport->fc_portname.u.wwn);
|
|
|
|
if (ls_rqst->rqst_len < FCNVME_LS_CA_CMD_MIN_LEN) {
|
|
SPDK_ERRLOG("assoc_cmd req len = %d, should be at least %d\n",
|
|
ls_rqst->rqst_len, FCNVME_LS_CA_CMD_MIN_LEN);
|
|
errmsg_ind = VERR_CR_ASSOC_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (from_be32(&rqst->desc_list_len) <
|
|
FCNVME_LS_CA_DESC_LIST_MIN_LEN) {
|
|
SPDK_ERRLOG("assoc_cmd desc list len = %d, should be at least %d\n",
|
|
from_be32(&rqst->desc_list_len),
|
|
FCNVME_LS_CA_DESC_LIST_MIN_LEN);
|
|
errmsg_ind = VERR_CR_ASSOC_RQST_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (rqst->assoc_cmd.desc_tag !=
|
|
cpu_to_be32(FCNVME_LSDESC_CREATE_ASSOC_CMD)) {
|
|
errmsg_ind = VERR_CR_ASSOC_CMD;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
} else if (from_be32(&rqst->assoc_cmd.desc_len) <
|
|
FCNVME_LS_CA_DESC_MIN_LEN) {
|
|
SPDK_ERRLOG("assoc_cmd desc len = %d, should be at least %d\n",
|
|
from_be32(&rqst->assoc_cmd.desc_len),
|
|
FCNVME_LS_CA_DESC_MIN_LEN);
|
|
errmsg_ind = VERR_CR_ASSOC_CMD_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (!rqst->assoc_cmd.ersp_ratio ||
|
|
(from_be16(&rqst->assoc_cmd.ersp_ratio) >=
|
|
from_be16(&rqst->assoc_cmd.sqsize))) {
|
|
errmsg_ind = VERR_ERSP_RATIO;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_ESRP;
|
|
} else if (from_be16(&rqst->assoc_cmd.sqsize) == 0 ||
|
|
from_be16(&rqst->assoc_cmd.sqsize) > transport->opts.max_aq_depth) {
|
|
errmsg_ind = VERR_SQSIZE;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_SQ_SIZE;
|
|
}
|
|
|
|
if (rc != FCNVME_RJT_RC_NONE) {
|
|
goto rjt_cass;
|
|
}
|
|
|
|
subsystem = spdk_nvmf_tgt_find_subsystem(ls_rqst->nvmf_tgt, rqst->assoc_cmd.subnqn);
|
|
if (subsystem == NULL) {
|
|
errmsg_ind = VERR_SUBNQN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_SUBNQN;
|
|
goto rjt_cass;
|
|
}
|
|
|
|
if (nvmf_fc_ls_validate_host(subsystem, hostnqn)) {
|
|
errmsg_ind = VERR_HOSTNQN;
|
|
rc = FCNVME_RJT_RC_INV_HOST;
|
|
ec = FCNVME_RJT_EXP_INV_HOSTNQN;
|
|
goto rjt_cass;
|
|
}
|
|
|
|
/* get new association */
|
|
assoc = nvmf_fc_ls_new_association(s_id, tgtport, ls_rqst->rport,
|
|
&rqst->assoc_cmd, subsystem,
|
|
ls_rqst->rpi, transport);
|
|
if (!assoc) {
|
|
errmsg_ind = VERR_ASSOC_ALLOC_FAIL;
|
|
rc = FCNVME_RJT_RC_INSUFF_RES;
|
|
ec = FCNVME_RJT_EXP_NONE;
|
|
goto rjt_cass;
|
|
}
|
|
|
|
/* alloc admin q (i.e. connection) */
|
|
fc_conn = nvmf_fc_ls_new_connection(assoc, 0,
|
|
from_be16(&rqst->assoc_cmd.ersp_ratio),
|
|
ls_rqst->rpi,
|
|
from_be16(&rqst->assoc_cmd.sqsize),
|
|
tgtport);
|
|
if (!fc_conn) {
|
|
nvmf_fc_ls_free_association(assoc);
|
|
errmsg_ind = VERR_CONN_ALLOC_FAIL;
|
|
rc = FCNVME_RJT_RC_INSUFF_RES;
|
|
ec = FCNVME_RJT_EXP_NONE;
|
|
goto rjt_cass;
|
|
}
|
|
|
|
/* format accept response */
|
|
bzero(acc, sizeof(*acc));
|
|
ls_rqst->rsp_len = sizeof(*acc);
|
|
|
|
nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC,
|
|
nvmf_fc_lsdesc_len(
|
|
sizeof(struct spdk_nvmf_fc_ls_cr_assoc_acc)),
|
|
FCNVME_LS_CREATE_ASSOCIATION);
|
|
to_be32(&acc->assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID);
|
|
acc->assoc_id.desc_len =
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id));
|
|
to_be32(&acc->conn_id.desc_tag, FCNVME_LSDESC_CONN_ID);
|
|
acc->conn_id.desc_len =
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_conn_id));
|
|
|
|
/* assign connection to HWQP poller - also sends response */
|
|
nvmf_fc_ls_add_conn_to_poller(assoc, ls_rqst, fc_conn, true);
|
|
|
|
return;
|
|
|
|
rjt_cass:
|
|
SPDK_ERRLOG("Create Association LS failed: %s\n", validation_errors[errmsg_ind]);
|
|
ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE,
|
|
rqst->w0.ls_cmd, rc, ec, 0);
|
|
nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_ls_process_cioc(struct spdk_nvmf_fc_nport *tgtport,
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_cr_conn_rqst *rqst =
|
|
(struct spdk_nvmf_fc_ls_cr_conn_rqst *)ls_rqst->rqstbuf.virt;
|
|
struct spdk_nvmf_fc_ls_cr_conn_acc *acc =
|
|
(struct spdk_nvmf_fc_ls_cr_conn_acc *)ls_rqst->rspbuf.virt;
|
|
struct spdk_nvmf_fc_association *assoc;
|
|
struct spdk_nvmf_fc_conn *fc_conn = NULL;
|
|
int errmsg_ind = 0;
|
|
uint8_t rc = FCNVME_RJT_RC_NONE;
|
|
uint8_t ec = FCNVME_RJT_EXP_NONE;
|
|
struct spdk_nvmf_transport *transport = spdk_nvmf_tgt_get_transport(ls_rqst->nvmf_tgt,
|
|
(enum spdk_nvme_transport_type) SPDK_NVMF_TRTYPE_FC);
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"LS_CIOC: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d, "
|
|
"assoc_id=0x%lx, sq_size=%d, esrp=%d, Tgtport nn:%lx, pn:%lx\n",
|
|
ls_rqst->rqst_len, from_be32(&rqst->desc_list_len),
|
|
from_be32(&rqst->connect_cmd.desc_len),
|
|
from_be64(&rqst->assoc_id.association_id),
|
|
from_be32(&rqst->connect_cmd.sqsize),
|
|
from_be32(&rqst->connect_cmd.ersp_ratio),
|
|
tgtport->fc_nodename.u.wwn, tgtport->fc_portname.u.wwn);
|
|
|
|
if (ls_rqst->rqst_len < sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst)) {
|
|
errmsg_ind = VERR_CR_CONN_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (rqst->desc_list_len !=
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst))) {
|
|
errmsg_ind = VERR_CR_CONN_RQST_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (rqst->assoc_id.desc_tag !=
|
|
cpu_to_be32(FCNVME_LSDESC_ASSOC_ID)) {
|
|
errmsg_ind = VERR_ASSOC_ID;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
} else if (rqst->assoc_id.desc_len !=
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id))) {
|
|
errmsg_ind = VERR_ASSOC_ID_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (rqst->connect_cmd.desc_tag !=
|
|
cpu_to_be32(FCNVME_LSDESC_CREATE_CONN_CMD)) {
|
|
errmsg_ind = VERR_CR_CONN_CMD;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
} else if (rqst->connect_cmd.desc_len !=
|
|
nvmf_fc_lsdesc_len(
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_cr_conn_cmd))) {
|
|
errmsg_ind = VERR_CR_CONN_CMD_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (!rqst->connect_cmd.ersp_ratio ||
|
|
(from_be16(&rqst->connect_cmd.ersp_ratio) >=
|
|
from_be16(&rqst->connect_cmd.sqsize))) {
|
|
errmsg_ind = VERR_ERSP_RATIO;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_ESRP;
|
|
} else if (from_be16(&rqst->connect_cmd.sqsize) == 0 ||
|
|
from_be16(&rqst->connect_cmd.sqsize) > transport->opts.max_queue_depth) {
|
|
errmsg_ind = VERR_SQSIZE;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_SQ_SIZE;
|
|
}
|
|
|
|
if (rc != FCNVME_RJT_RC_NONE) {
|
|
goto rjt_cioc;
|
|
}
|
|
|
|
/* find association */
|
|
assoc = nvmf_fc_ls_find_assoc(tgtport,
|
|
from_be64(&rqst->assoc_id.association_id));
|
|
if (!assoc) {
|
|
errmsg_ind = VERR_NO_ASSOC;
|
|
rc = FCNVME_RJT_RC_INV_ASSOC;
|
|
} else if (assoc->assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) {
|
|
/* association is being deleted - don't allow more connections */
|
|
errmsg_ind = VERR_NO_ASSOC;
|
|
rc = FCNVME_RJT_RC_INV_ASSOC;
|
|
} else if (assoc->conn_count >= transport->opts.max_qpairs_per_ctrlr) {
|
|
errmsg_ind = VERR_CONN_TOO_MANY;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_Q_ID;
|
|
}
|
|
|
|
if (rc != FCNVME_RJT_RC_NONE) {
|
|
goto rjt_cioc;
|
|
}
|
|
|
|
fc_conn = nvmf_fc_ls_new_connection(assoc, from_be16(&rqst->connect_cmd.qid),
|
|
from_be16(&rqst->connect_cmd.ersp_ratio),
|
|
ls_rqst->rpi,
|
|
from_be16(&rqst->connect_cmd.sqsize),
|
|
tgtport);
|
|
if (!fc_conn) {
|
|
errmsg_ind = VERR_CONN_ALLOC_FAIL;
|
|
rc = FCNVME_RJT_RC_INSUFF_RES;
|
|
ec = FCNVME_RJT_EXP_NONE;
|
|
goto rjt_cioc;
|
|
}
|
|
|
|
/* format accept response */
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Formatting LS accept response for "
|
|
"assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id,
|
|
fc_conn->conn_id);
|
|
bzero(acc, sizeof(*acc));
|
|
ls_rqst->rsp_len = sizeof(*acc);
|
|
nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC,
|
|
nvmf_fc_lsdesc_len(
|
|
sizeof(struct spdk_nvmf_fc_ls_cr_conn_acc)),
|
|
FCNVME_LS_CREATE_CONNECTION);
|
|
to_be32(&acc->conn_id.desc_tag, FCNVME_LSDESC_CONN_ID);
|
|
acc->conn_id.desc_len =
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_conn_id));
|
|
|
|
/* assign connection to HWQP poller - also sends response */
|
|
nvmf_fc_ls_add_conn_to_poller(assoc, ls_rqst, fc_conn, false);
|
|
|
|
return;
|
|
|
|
rjt_cioc:
|
|
SPDK_ERRLOG("Create Connection LS failed: %s\n", validation_errors[errmsg_ind]);
|
|
|
|
ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE,
|
|
rqst->w0.ls_cmd, rc, ec, 0);
|
|
nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_ls_process_disc(struct spdk_nvmf_fc_nport *tgtport,
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_disconnect_rqst *rqst =
|
|
(struct spdk_nvmf_fc_ls_disconnect_rqst *)ls_rqst->rqstbuf.virt;
|
|
struct spdk_nvmf_fc_ls_disconnect_acc *acc =
|
|
(struct spdk_nvmf_fc_ls_disconnect_acc *)ls_rqst->rspbuf.virt;
|
|
struct spdk_nvmf_fc_association *assoc;
|
|
int errmsg_ind = 0;
|
|
uint8_t rc = FCNVME_RJT_RC_NONE;
|
|
uint8_t ec = FCNVME_RJT_EXP_NONE;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
|
|
"LS_DISC: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d,"
|
|
"assoc_id=0x%lx\n",
|
|
ls_rqst->rqst_len, from_be32(&rqst->desc_list_len),
|
|
from_be32(&rqst->disconn_cmd.desc_len),
|
|
from_be64(&rqst->assoc_id.association_id));
|
|
|
|
if (ls_rqst->rqst_len < sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst)) {
|
|
errmsg_ind = VERR_DISCONN_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (rqst->desc_list_len !=
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst))) {
|
|
errmsg_ind = VERR_DISCONN_RQST_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (rqst->assoc_id.desc_tag !=
|
|
cpu_to_be32(FCNVME_LSDESC_ASSOC_ID)) {
|
|
errmsg_ind = VERR_ASSOC_ID;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
} else if (rqst->assoc_id.desc_len !=
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id))) {
|
|
errmsg_ind = VERR_ASSOC_ID_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
} else if (rqst->disconn_cmd.desc_tag !=
|
|
cpu_to_be32(FCNVME_LSDESC_DISCONN_CMD)) {
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
errmsg_ind = VERR_DISCONN_CMD;
|
|
} else if (rqst->disconn_cmd.desc_len !=
|
|
nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_disconn_cmd))) {
|
|
errmsg_ind = VERR_DISCONN_CMD_LEN;
|
|
rc = FCNVME_RJT_RC_INV_PARAM;
|
|
ec = FCNVME_RJT_EXP_INV_LEN;
|
|
}
|
|
|
|
if (rc != FCNVME_RJT_RC_NONE) {
|
|
goto rjt_disc;
|
|
}
|
|
|
|
/* match an active association */
|
|
assoc = nvmf_fc_ls_find_assoc(tgtport,
|
|
from_be64(&rqst->assoc_id.association_id));
|
|
if (!assoc) {
|
|
errmsg_ind = VERR_NO_ASSOC;
|
|
rc = FCNVME_RJT_RC_INV_ASSOC;
|
|
goto rjt_disc;
|
|
}
|
|
|
|
/* format response */
|
|
bzero(acc, sizeof(*acc));
|
|
ls_rqst->rsp_len = sizeof(*acc);
|
|
|
|
nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC,
|
|
nvmf_fc_lsdesc_len(
|
|
sizeof(struct spdk_nvmf_fc_ls_disconnect_acc)),
|
|
FCNVME_LS_DISCONNECT);
|
|
|
|
nvmf_fc_ls_disconnect_assoc(tgtport, ls_rqst, assoc->assoc_id);
|
|
return;
|
|
|
|
rjt_disc:
|
|
SPDK_ERRLOG("Disconnect LS failed: %s\n", validation_errors[errmsg_ind]);
|
|
ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE,
|
|
rqst->w0.ls_cmd, rc, ec, 0);
|
|
nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
|
|
}
|
|
|
|
/* ************************ */
|
|
/* external functions */
|
|
|
|
void
|
|
spdk_nvmf_fc_ls_init(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
}
|
|
|
|
void
|
|
spdk_nvmf_fc_ls_fini(struct spdk_nvmf_fc_port *fc_port)
|
|
{
|
|
}
|
|
|
|
void
|
|
spdk_nvmf_fc_handle_ls_rqst(struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_rqst_w0 *w0 =
|
|
(struct spdk_nvmf_fc_ls_rqst_w0 *)ls_rqst->rqstbuf.virt;
|
|
uint32_t s_id = ls_rqst->s_id;
|
|
struct spdk_nvmf_fc_nport *tgtport = ls_rqst->nport;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "LS cmd=%d\n", w0->ls_cmd);
|
|
|
|
switch (w0->ls_cmd) {
|
|
case FCNVME_LS_CREATE_ASSOCIATION:
|
|
nvmf_fc_ls_process_cass(s_id, tgtport, ls_rqst);
|
|
break;
|
|
case FCNVME_LS_CREATE_CONNECTION:
|
|
nvmf_fc_ls_process_cioc(tgtport, ls_rqst);
|
|
break;
|
|
case FCNVME_LS_DISCONNECT:
|
|
nvmf_fc_ls_process_disc(tgtport, ls_rqst);
|
|
break;
|
|
default:
|
|
SPDK_ERRLOG("Invalid LS cmd=%d\n", w0->ls_cmd);
|
|
ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(ls_rqst->rspbuf.virt,
|
|
FCNVME_MAX_LS_BUFFER_SIZE, w0->ls_cmd,
|
|
FCNVME_RJT_RC_INVAL, FCNVME_RJT_EXP_NONE, 0);
|
|
nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
|
|
}
|
|
}
|
|
|
|
int
|
|
spdk_nvmf_fc_delete_association(struct spdk_nvmf_fc_nport *tgtport,
|
|
uint64_t assoc_id, bool send_abts, bool backend_initiated,
|
|
spdk_nvmf_fc_del_assoc_cb del_assoc_cb,
|
|
void *cb_data)
|
|
{
|
|
return nvmf_fc_delete_association(tgtport, assoc_id, send_abts, backend_initiated,
|
|
del_assoc_cb, cb_data, false);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_cb_event(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_cb_info *cb_info =
|
|
(struct spdk_nvmf_fc_poller_api_cb_info *) arg;
|
|
|
|
assert(cb_info != NULL);
|
|
cb_info->cb_func(cb_info->cb_data, cb_info->ret);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_perform_cb(struct spdk_nvmf_fc_poller_api_cb_info *cb_info,
|
|
enum spdk_nvmf_fc_poller_api_ret ret)
|
|
{
|
|
if (cb_info->cb_func && cb_info->cb_thread) {
|
|
cb_info->ret = ret;
|
|
/* callback to master thread */
|
|
spdk_thread_send_msg(cb_info->cb_thread, nvmf_fc_poller_api_cb_event,
|
|
(void *) cb_info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_add_connection(void *arg)
|
|
{
|
|
enum spdk_nvmf_fc_poller_api_ret ret = SPDK_NVMF_FC_POLLER_API_SUCCESS;
|
|
struct spdk_nvmf_fc_poller_api_add_connection_args *conn_args =
|
|
(struct spdk_nvmf_fc_poller_api_add_connection_args *)arg;
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Poller add connection, conn_id 0x%lx\n",
|
|
conn_args->fc_conn->conn_id);
|
|
|
|
/* make sure connection is not already in poller's list */
|
|
fc_conn = spdk_nvmf_fc_hwqp_find_fc_conn(conn_args->fc_conn->hwqp,
|
|
conn_args->fc_conn->conn_id);
|
|
if (fc_conn) {
|
|
SPDK_ERRLOG("duplicate connection found");
|
|
ret = SPDK_NVMF_FC_POLLER_API_DUP_CONN_ID;
|
|
} else {
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
|
|
"conn_id=%lx", fc_conn->conn_id);
|
|
TAILQ_INSERT_TAIL(&conn_args->fc_conn->hwqp->connection_list,
|
|
conn_args->fc_conn, link);
|
|
}
|
|
|
|
/* perform callback */
|
|
nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, ret);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_quiesce_queue(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_quiesce_queue_args *q_args =
|
|
(struct spdk_nvmf_fc_poller_api_quiesce_queue_args *) arg;
|
|
struct spdk_nvmf_fc_request *fc_req = NULL, *tmp;
|
|
|
|
/* should be already, but make sure queue is quiesced */
|
|
q_args->hwqp->state = SPDK_FC_HWQP_OFFLINE;
|
|
|
|
/*
|
|
* Kill all the outstanding commands that are in the transfer state and
|
|
* in the process of being aborted.
|
|
* We can run into this situation if an adapter reset happens when an I_T Nexus delete
|
|
* is in progress.
|
|
*/
|
|
TAILQ_FOREACH_SAFE(fc_req, &q_args->hwqp->in_use_reqs, link, tmp) {
|
|
if (spdk_nvmf_fc_req_in_xfer(fc_req) && fc_req->is_aborted == true) {
|
|
spdk_nvmf_fc_poller_api_func(q_args->hwqp, SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE,
|
|
(void *)fc_req);
|
|
}
|
|
}
|
|
|
|
/* perform callback */
|
|
nvmf_fc_poller_api_perform_cb(&q_args->cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_activate_queue(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_quiesce_queue_args *q_args =
|
|
(struct spdk_nvmf_fc_poller_api_quiesce_queue_args *) arg;
|
|
|
|
q_args->hwqp->state = SPDK_FC_HWQP_ONLINE;
|
|
|
|
/* perform callback */
|
|
nvmf_fc_poller_api_perform_cb(&q_args->cb_info, 0);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_disconnect_qpair_cb(void *ctx)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_cb_info *cb_info = ctx;
|
|
/* perform callback */
|
|
nvmf_fc_poller_api_perform_cb(cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_conn_abort_done(void *hwqp, int32_t status, void *cb_args)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_del_connection_args *conn_args = cb_args;
|
|
|
|
if (conn_args->fc_request_cnt) {
|
|
conn_args->fc_request_cnt -= 1;
|
|
}
|
|
|
|
if (!conn_args->fc_request_cnt) {
|
|
if (!TAILQ_EMPTY(&conn_args->hwqp->connection_list)) {
|
|
/* All the requests for this connection are aborted. */
|
|
TAILQ_REMOVE(&conn_args->hwqp->connection_list, conn_args->fc_conn, link);
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Connection deleted, conn_id 0x%lx\n",
|
|
conn_args->fc_conn->conn_id);
|
|
|
|
if (!conn_args->backend_initiated) {
|
|
/* disconnect qpair from nvmf controller */
|
|
spdk_nvmf_qpair_disconnect(&conn_args->fc_conn->qpair,
|
|
nvmf_fc_disconnect_qpair_cb, &conn_args->cb_info);
|
|
}
|
|
} else {
|
|
/*
|
|
* Duplicate connection delete can happen if one is
|
|
* coming in via an association disconnect and the other
|
|
* is initiated by a port reset.
|
|
*/
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Duplicate conn delete.");
|
|
/* perform callback */
|
|
nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_del_connection(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_del_connection_args *conn_args =
|
|
(struct spdk_nvmf_fc_poller_api_del_connection_args *)arg;
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
struct spdk_nvmf_fc_request *fc_req = NULL, *tmp;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = conn_args->hwqp;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Poller delete connection, conn_id 0x%lx\n",
|
|
conn_args->fc_conn->conn_id);
|
|
|
|
/* find the connection in poller's list */
|
|
fc_conn = spdk_nvmf_fc_hwqp_find_fc_conn(hwqp, conn_args->fc_conn->conn_id);
|
|
if (!fc_conn) {
|
|
/* perform callback */
|
|
nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, SPDK_NVMF_FC_POLLER_API_NO_CONN_ID);
|
|
return;
|
|
}
|
|
|
|
conn_args->fc_request_cnt = 0;
|
|
|
|
TAILQ_FOREACH_SAFE(fc_req, &hwqp->in_use_reqs, link, tmp) {
|
|
if (fc_req->fc_conn->conn_id == fc_conn->conn_id) {
|
|
if (spdk_nvmf_qpair_is_admin_queue(&fc_conn->qpair) &&
|
|
(fc_req->req.cmd->nvme_cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST)) {
|
|
/* AER will be cleaned by spdk_nvmf_qpair_disconnect. */
|
|
continue;
|
|
}
|
|
|
|
conn_args->fc_request_cnt += 1;
|
|
spdk_nvmf_fc_request_abort(fc_req, conn_args->send_abts,
|
|
nvmf_fc_poller_conn_abort_done,
|
|
conn_args);
|
|
}
|
|
}
|
|
|
|
if (!conn_args->fc_request_cnt) {
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Connection deleted.\n");
|
|
TAILQ_REMOVE(&hwqp->connection_list, fc_conn, link);
|
|
|
|
if (!conn_args->backend_initiated) {
|
|
/* disconnect qpair from nvmf controller */
|
|
spdk_nvmf_qpair_disconnect(&fc_conn->qpair, nvmf_fc_disconnect_qpair_cb,
|
|
&conn_args->cb_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_abts_done(void *hwqp, int32_t status, void *cb_args)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_abts_recvd_args *args = cb_args;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
|
|
"ABTS poller done, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
|
|
args->ctx->rpi, args->ctx->oxid, args->ctx->rxid);
|
|
|
|
nvmf_fc_poller_api_perform_cb(&args->cb_info,
|
|
SPDK_NVMF_FC_POLLER_API_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_abts_received(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_abts_recvd_args *args = arg;
|
|
struct spdk_nvmf_fc_request *fc_req = NULL;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = args->hwqp;
|
|
|
|
TAILQ_FOREACH(fc_req, &hwqp->in_use_reqs, link) {
|
|
if ((fc_req->rpi == args->ctx->rpi) &&
|
|
(fc_req->oxid == args->ctx->oxid)) {
|
|
spdk_nvmf_fc_request_abort(fc_req, false,
|
|
nvmf_fc_poller_abts_done, args);
|
|
return;
|
|
}
|
|
}
|
|
|
|
nvmf_fc_poller_api_perform_cb(&args->cb_info,
|
|
SPDK_NVMF_FC_POLLER_API_OXID_NOT_FOUND);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_queue_sync(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_queue_sync_args *args = arg;
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
|
|
"HWQP sync requested for u_id = 0x%lx\n", args->u_id);
|
|
|
|
/* Add this args to hwqp sync_cb list */
|
|
TAILQ_INSERT_TAIL(&args->hwqp->sync_cbs, args, link);
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_queue_sync_done(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_poller_api_queue_sync_done_args *args = arg;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = args->hwqp;
|
|
uint64_t tag = args->tag;
|
|
struct spdk_nvmf_fc_poller_api_queue_sync_args *sync_args = NULL, *tmp = NULL;
|
|
|
|
assert(args != NULL);
|
|
|
|
TAILQ_FOREACH_SAFE(sync_args, &hwqp->sync_cbs, link, tmp) {
|
|
if (sync_args->u_id == tag) {
|
|
/* Queue successfully synced. Remove from cb list */
|
|
TAILQ_REMOVE(&hwqp->sync_cbs, sync_args, link);
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
|
|
"HWQP sync done for u_id = 0x%lx\n", sync_args->u_id);
|
|
|
|
/* Return the status to poller */
|
|
nvmf_fc_poller_api_perform_cb(&sync_args->cb_info,
|
|
SPDK_NVMF_FC_POLLER_API_SUCCESS);
|
|
return;
|
|
}
|
|
}
|
|
|
|
free(arg);
|
|
/* note: no callback from this api */
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_add_hwqp(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_hwqp *hwqp = (struct spdk_nvmf_fc_hwqp *)arg;
|
|
|
|
hwqp->lcore_id = spdk_env_get_current_core(); /* for tracing purposes only */
|
|
TAILQ_INSERT_TAIL(&hwqp->fc_poll_group->hwqp_list, hwqp, link);
|
|
/* note: no callback from this api */
|
|
}
|
|
|
|
static void
|
|
nvmf_fc_poller_api_remove_hwqp(void *arg)
|
|
{
|
|
struct spdk_nvmf_fc_hwqp *hwqp = (struct spdk_nvmf_fc_hwqp *)arg;
|
|
struct spdk_nvmf_fc_poll_group *fc_poll_group = hwqp->fc_poll_group;
|
|
|
|
TAILQ_REMOVE(&fc_poll_group->hwqp_list, hwqp, link);
|
|
hwqp->fc_poll_group = NULL;
|
|
/* note: no callback from this api */
|
|
}
|
|
|
|
enum spdk_nvmf_fc_poller_api_ret
|
|
spdk_nvmf_fc_poller_api_func(struct spdk_nvmf_fc_hwqp *hwqp, enum spdk_nvmf_fc_poller_api api,
|
|
void *api_args) {
|
|
switch (api)
|
|
{
|
|
case SPDK_NVMF_FC_POLLER_API_ADD_CONNECTION:
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
nvmf_fc_poller_api_add_connection, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION:
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
nvmf_fc_poller_api_del_connection, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_QUIESCE_QUEUE:
|
|
/* quiesce q polling now, don't wait for poller to do it */
|
|
hwqp->state = SPDK_FC_HWQP_OFFLINE;
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
nvmf_fc_poller_api_quiesce_queue, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_ACTIVATE_QUEUE:
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
nvmf_fc_poller_api_activate_queue, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_ABTS_RECEIVED:
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
nvmf_fc_poller_api_abts_received, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE:
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
spdk_nvmf_fc_request_abort_complete, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_QUEUE_SYNC:
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
nvmf_fc_poller_api_queue_sync, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_QUEUE_SYNC_DONE:
|
|
spdk_thread_send_msg(hwqp->thread,
|
|
nvmf_fc_poller_api_queue_sync_done, api_args);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_ADD_HWQP:
|
|
spdk_thread_send_msg(hwqp->thread, nvmf_fc_poller_api_add_hwqp, (void *) hwqp);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_REMOVE_HWQP:
|
|
spdk_thread_send_msg(hwqp->thread, nvmf_fc_poller_api_remove_hwqp, (void *) hwqp);
|
|
break;
|
|
|
|
case SPDK_NVMF_FC_POLLER_API_ADAPTER_EVENT:
|
|
case SPDK_NVMF_FC_POLLER_API_AEN:
|
|
default:
|
|
SPDK_ERRLOG("BAD ARG!");
|
|
return SPDK_NVMF_FC_POLLER_API_INVALID_ARG;
|
|
}
|
|
|
|
return SPDK_NVMF_FC_POLLER_API_SUCCESS;
|
|
}
|
|
|
|
SPDK_LOG_REGISTER_COMPONENT("nvmf_fc_poller_api", SPDK_LOG_NVMF_FC_POLLER_API)
|
|
SPDK_LOG_REGISTER_COMPONENT("nvmf_fc_ls", SPDK_LOG_NVMF_FC_LS)
|