diff options
Diffstat (limited to 'src/spdk/lib/nvmf/fc_ls.c')
-rw-r--r-- | src/spdk/lib/nvmf/fc_ls.c | 1678 |
1 files changed, 1678 insertions, 0 deletions
diff --git a/src/spdk/lib/nvmf/fc_ls.c b/src/spdk/lib/nvmf/fc_ls.c new file mode 100644 index 000000000..1aa06bd45 --- /dev/null +++ b/src/spdk/lib/nvmf/fc_ls.c @@ -0,0 +1,1678 @@ +/* + * 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, SPDK_NVME_NQN_FIELD_SIZE); + memcpy(assoc->sub_nqn, a_cmd->subnqn, SPDK_NVME_NQN_FIELD_SIZE); + 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) + */ + 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; + + 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; + + 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; + spdk_nvmf_tgt_new_qpair(ls_rqst->nvmf_tgt, &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 (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; + 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, + SPDK_NVME_TRANSPORT_NAME_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, + SPDK_NVME_TRANSPORT_NAME_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 +nvmf_fc_ls_init(struct spdk_nvmf_fc_port *fc_port) +{ +} + +void +nvmf_fc_ls_fini(struct spdk_nvmf_fc_port *fc_port) +{ +} + +void +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 +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 = 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 (nvmf_fc_req_in_xfer(fc_req) && fc_req->is_aborted == true) { + 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 = 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 (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; + 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)) { + 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 +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, + 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) |