0a04c076ea
It can be useful for passing additional information about nvmf target to a handler for new nvmf connections. Context can be stored in globals as it is currently done in nvmf code. However in case of multiple targets or languages where accessing global state is challenging (i.e. Rust), this becomes inconvenient. Change-Id: Ia6a2fdba4601531822b3e5fda7ac5ab89d46f6c5 Signed-off-by: Jan Kryl <jan.kryl@mayadata.io> Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/469263 Community-CI: Broadcom SPDK FC-NVMe CI <spdk-ci.pdl@broadcom.com> Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Seth Howell <seth.howell@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Sasha Kotchubievsky <sashakot@mellanox.com>
1681 lines
52 KiB
C
1681 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);
|
|
|
|
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, fc_port->new_qp_arg);
|
|
}
|
|
|
|
/* 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->fgroup->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 *fgroup = hwqp->fgroup;
|
|
|
|
TAILQ_REMOVE(&fgroup->hwqp_list, hwqp, link);
|
|
hwqp->fgroup = 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)
|