[PATCH 04/14] scsi: fnic: Add support for target based solicited requests and responses

From: Karan Tilak Kumar
Date: Mon Jun 10 2024 - 17:54:39 EST


Add support for target based solicited requests and responses.
Add support for tport definitions and processing.
Add support for restarting the IT nexus.

Reviewed-by: Sesidhar Baddela <sebaddel@xxxxxxxxx>
Reviewed-by: Arulprabhu Ponnusamy <arulponn@xxxxxxxxx>
Reviewed-by: Gian Carlo Boffa <gcboffa@xxxxxxxxx>
Signed-off-by: Karan Tilak Kumar <kartilak@xxxxxxxxx>
---
drivers/scsi/fnic/fdls_disc.c | 1540 +++++++++++++++++++++++++++++++--
drivers/scsi/fnic/fnic.h | 6 +
drivers/scsi/fnic/fnic_fdls.h | 4 +
3 files changed, 1473 insertions(+), 77 deletions(-)

diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index 22a2d0c1c78f..d920202d413d 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -11,6 +11,8 @@
#include <scsi/fc/fc_fcp.h>
#include <linux/utsname.h>

+#define FC_FC4_TYPE_SCSI 0x08
+
static void fdls_send_rpn_id(struct fnic_iport_s *iport);

/* Frame initialization */
@@ -59,6 +61,19 @@ struct fc_rpn_id_s fnic_rpn_id_req = {
.command = FC_CT_RPN_CMD}
};

+/*
+ * Variables:
+ * did, sid, oxid
+ */
+struct fc_els_prli_s fnic_prli_req = {
+ .fchdr = {.r_ctl = 0x22, .type = 0x01,
+ .f_ctl = FNIC_ELS_REQ_FCTL, .rx_id = 0xFFFF},
+ .command = FC_ELS_PRLI_REQ,
+ .page_len = 16,
+ .payload_len = 0x1400,
+ .sp = {.type = 0x08, .flags = 0x0020, .csp = 0xA2000000}
+};
+
/*
* Variables:
* fh_s_id, port_id, port_name
@@ -123,11 +138,51 @@ struct fc_logo_req_s fnic_logo_req = {
#define RETRIES_EXHAUSTED(iport) \
(iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)

+#define FNIC_TPORT_MAX_NEXUS_RESTART (8)
+
+/* Private Functions */
static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
struct fc_hdr_s *fchdr, void *rx_frame);
static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+ uint32_t fcid,
+ uint64_t wwpn);
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport);
+static void fdls_start_tport_timer(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, int timeout);
+
static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
int timeout);
+static void fdls_tport_timer_callback(struct timer_list *t);
+
+static uint16_t fdls_alloc_tgt_oxid(struct fnic_iport_s *iport,
+ uint16_t base)
+{
+ int i;
+ int start, end;
+
+ start = base - FDLS_PLOGI_OXID_BASE;
+ end = start + FDLS_TGT_OXID_BLOCK_SZ;
+
+ for (i = start; i < end; i++) {
+ if (iport->tgt_oxid_pool[i] == 0) {
+ iport->tgt_oxid_pool[i] = 1;
+ return (i + FDLS_PLOGI_OXID_BASE);
+ }
+ }
+ return 0xFFFF;
+}
+
+static void fdls_free_tgt_oxid(struct fnic_iport_s *iport, uint16_t oxid)
+{
+ struct fnic *fnic = iport->fnic;
+
+ if (iport->tgt_oxid_pool[oxid - FDLS_PLOGI_OXID_BASE] != 1) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Freeing unused OXID: 0x%x", oxid);
+ }
+ iport->tgt_oxid_pool[oxid - FDLS_PLOGI_OXID_BASE] = 0;
+}

static void
fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
@@ -153,6 +208,63 @@ fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
"fabric timer is %d ", timeout);
}

+static void
+fdls_start_tport_timer(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, int timeout)
+{
+ u64 fabric_tov;
+ struct fnic *fnic = iport->fnic;
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync();
+ tport->timer_pending = 0;
+ }
+
+ if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED))
+ tport->retry_counter++;
+
+ fabric_tov = jiffies + msecs_to_jiffies(timeout);
+ mod_timer(&tport->retry_timer, round_jiffies(fabric_tov));
+ tport->timer_pending = 1;
+}
+
+void
+fdls_send_tport_abts(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport)
+{
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fnic *fnic = iport->fnic;
+
+ struct fc_hdr_s fc_abts_s = {
+ .r_ctl = 0x81, /* ABTS */
+ .cs_ctl = 0x00, .type = 0x00, .f_ctl = FNIC_REQ_ABTS_FCTL, .seq_id =
+ 0x00, .df_ctl = 0x00, .seq_cnt = 0x0000, .rx_id = 0xFFFF,
+ .param = 0x00000000, /* bit:0 = 0 Abort a exchange */
+ };
+
+ struct fc_hdr_s *pfc_abts = &fc_abts_s;
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+ FNIC_SET_S_ID(pfc_abts, s_id);
+ FNIC_SET_D_ID(pfc_abts, d_id);
+ tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ fc_abts_s.ox_id = tport->oxid_used;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "FDLS sending tport abts: tport->state: %d ",
+ tport->state);
+
+ fnic_send_fcoe_frame(iport, &fc_abts_s, sizeof(struct fc_hdr_s));
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, 2 * iport->r_a_tov);
+}
+
static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
{
uint8_t fcid[3];
@@ -338,6 +450,169 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
fdls_set_state((&iport->fabric), fdls_state);
}

+static void
+fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ struct fc_els_adisc_s adisc;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+
+ memset(&adisc, 0, sizeof(struct fc_els_adisc_s));
+ adisc.fchdr.r_ctl = 0x22;
+ adisc.fchdr.type = 0x01;
+ adisc.fchdr.f_ctl = FNIC_ELS_REQ_FCTL;
+ adisc.fchdr.rx_id = 0xFFFF;
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+ FNIC_SET_S_ID((&adisc.fchdr), s_id);
+ FNIC_SET_D_ID((&adisc.fchdr), d_id);
+
+ oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_ADISC_OXID_BASE));
+ if (oxid == 0xFFFF) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Failed to allocate OXID to send ADISC %p", iport);
+ return;
+ }
+
+ tport->oxid_used = oxid;
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ FNIC_SET_OX_ID((&adisc.fchdr), oxid);
+ FNIC_SET_NPORT_NAME(adisc, iport->wwpn);
+ FNIC_SET_NODE_NAME(adisc, iport->wwnn);
+
+ memcpy(adisc.fcid, s_id, 3);
+ adisc.command = FNIC_ELS_ADISC_REQ;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "sending ADISC to tgt fcid: 0x%x", tport->fcid);
+
+ fnic_send_fcoe_frame(iport, &adisc, sizeof(struct fc_els_adisc_s));
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+
+void fdls_delete_tport(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport)
+{
+ struct fnic_tport_event_s *tport_del_evt;
+ struct fnic *fnic = iport->fnic;
+
+ if ((tport->state == FDLS_TGT_STATE_OFFLINING)
+ || (tport->state == FDLS_TGT_STATE_OFFLINE))
+ return;
+
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
+ /*
+ * By setting this flag, the tport will not be seen in a look-up
+ * in an RSCN. Even if we move to multithreaded model, this tport
+ * will be destroyed and a new RSCN will have to create a new one
+ */
+ tport->flags |= FNIC_FDLS_TPORT_TERMINATING;
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync();
+ tport->timer_pending = 0;
+ }
+
+ if (IS_FNIC_FCP_INITIATOR(fnic)) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ fnic_rport_exch_reset(iport->fnic, tport->fcid);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+ if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) {
+ tport_del_evt =
+ kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+ if (!tport_del_evt) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Failed to allocate memory for tport fcid: 0x%0x\n",
+ tport->fcid);
+ return;
+ }
+ tport_del_evt->event = TGT_EV_RPORT_DEL;
+ tport_del_evt->arg1 = (void *) tport;
+ list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+ queue_work(fnic_event_queue, &fnic->tport_work);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport 0x%x not reg with scsi_transport. Freeing locally",
+ tport->fcid);
+ list_del(&tport->links);
+ kfree(tport);
+ }
+ }
+}
+
+static void
+fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ struct fc_els_s plogi;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+ uint32_t timeout;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Send tgt PLOGI to fcid: 0x%x", tport->fcid);
+
+ memcpy(&plogi, &fnic_plogi_req, sizeof(struct fc_els_s));
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+
+ FNIC_SET_S_ID((&plogi.fchdr), s_id);
+ FNIC_SET_D_ID((&plogi.fchdr), d_id);
+ FNIC_SET_RDF_SIZE(plogi.u.csp_plogi, iport->max_payload_size);
+
+ oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_PLOGI_OXID_BASE));
+ if (oxid == 0xFFFF) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
+ iport->fcid, tport->fcid);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "send tgt PLOGI: tgt fcid: 0x%x oxid: 0x%x", tport->fcid,
+ ntohs(oxid));
+ tport->oxid_used = oxid;
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ FNIC_SET_OX_ID((&plogi.fchdr), oxid);
+ FNIC_SET_NPORT_NAME(plogi, iport->wwpn);
+ FNIC_SET_NODE_NAME(plogi, iport->wwnn);
+
+ timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+ fnic_send_fcoe_frame(iport, &plogi, sizeof(struct fc_els_s));
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, timeout);
+}
+
+static uint16_t
+fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
+ struct fc_els_s *plogi_rsp)
+{
+ uint16_t b2b_rdf_size = ntohs(plogi_rsp->u.csp_plogi.b2b_rdf_size);
+ uint16_t spc3_rdf_size =
+ ((uint16_t) (plogi_rsp->spc3[6] << 8 | plogi_rsp->spc3[7]) &
+ FNIC_FC_C3_RDF);
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
+ b2b_rdf_size, spc3_rdf_size);
+
+ return MIN(b2b_rdf_size, spc3_rdf_size);
+}
+
static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
{
struct fc_rft_id rft_id;
@@ -387,6 +662,47 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

+static void
+fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ struct fc_els_prli_s prli;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+ uint32_t timeout;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "FDLS sending PRLI to tgt: 0x%x", tport->fcid);
+
+ oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_PRLI_OXID_BASE));
+ if (oxid == 0xFFFF) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Failed to allocate OXID to send PRLI %p", iport);
+ return;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "FDLS sending PRLI to tgt: 0x%x OXID: 0x%x", tport->fcid,
+ ntohs(oxid));
+
+ tport->oxid_used = oxid;
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+ memcpy(&prli, &fnic_prli_req, sizeof(struct fc_els_prli_s));
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+
+ FNIC_SET_S_ID((&prli.fchdr), s_id);
+ FNIC_SET_D_ID((&prli.fchdr), d_id);
+ FNIC_SET_OX_ID((&prli.fchdr), oxid);
+
+ timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+ fnic_send_fcoe_frame(iport, &prli, sizeof(struct fc_els_prli_s));
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, timeout);
+}
+
/***********************************************************************
* fdls_send_fabric_logo
*
@@ -434,6 +750,208 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport)
fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_logo_req_s));
}

+/***********************************************************************
+ * fdls_tgt_logout
+ *
+ * \brief Send plogo to the remote port
+ *
+ * \param[in] iport Handle to fnic iport. remote port
+ *
+ * \retval void
+ *
+ * \note This function does not change or check the fabric/tport state.
+ * It the caller responsibility to set the appropriate tport/fabric
+ * state when this is called. Normall fdls_tgt_state_plogo.
+ * fdls_set_tport_state(tport, fdls_tgt_state_plogo)
+ *
+ *\note This could be used to send plogo to nameserver process
+ * also not just target processes
+ *
+ ***********************************************************************/
+void fdls_tgt_logout(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport)
+{
+ struct fc_logo_req_s logo;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Sending logo to tport fcid: 0x%x", tport->fcid);
+ memcpy(&logo, &fnic_logo_req, sizeof(struct fc_logo_req_s));
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+
+ FNIC_SET_S_ID((&logo.fchdr), s_id);
+ FNIC_SET_D_ID((&logo.fchdr), d_id);
+ FNIC_SET_OX_ID((&logo.fchdr), FNIC_TLOGO_REQ_OXID);
+
+ memcpy(&logo.fcid, s_id, 3);
+ logo.wwpn = htonll(iport->wwpn);
+
+ fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_logo_req_s));
+}
+
+static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
+{
+ struct fnic_tport_s *tport, *next;
+ u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: Starting FDLS target discovery", iport->fcid);
+
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+ if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+ || (iport->state != FNIC_IPORT_STATE_READY)) {
+ break;
+ }
+ /* if we marked the tport as deleted due to GPN_FT
+ * We should not send ADISC anymore
+ */
+ if ((tport->state == FDLS_TGT_STATE_OFFLINING) ||
+ (tport->state == FDLS_TGT_STATE_OFFLINE))
+ continue;
+
+ /* For tports which have received RSCN */
+ if (tport->flags & FNIC_FDLS_TPORT_SEND_ADISC) {
+ tport->retry_counter = 0;
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_ADISC);
+ tport->flags &= ~FNIC_FDLS_TPORT_SEND_ADISC;
+ fdls_send_tgt_adisc(iport, tport);
+ continue;
+ }
+ if (fdls_get_tport_state(tport) != FDLS_TGT_STATE_INIT) {
+ /* Not a new port, skip */
+ continue;
+ }
+ tport->retry_counter = 0;
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+ fdls_send_tgt_plogi(iport, tport);
+ }
+ fdls_set_state((&iport->fabric), FDLS_STATE_TGT_DISCOVERY);
+}
+
+/*
+ * Function to restart the IT nexus if we received any out of
+ * sequence PLOGI/PRLI response from the target.
+ * The memory for the new tport structure is allocated
+ * inside fdls_create_tport and added to the iport's tport list.
+ * This will get freed later during tport_offline/linkdown
+ * or module unload. The new_tport pointer will go out of scope
+ * safely since the memory it is
+ * pointing to it will be freed later
+ */
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
+{
+ struct fnic_iport_s *iport = tport->iport;
+ struct fnic_tport_s *new_tport = NULL;
+ uint32_t fcid;
+ uint64_t wwpn;
+ int nexus_restart_count;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport fcid: 0x%x state: %d restart_count: %d",
+ tport->fcid, tport->state, tport->nexus_restart_count);
+
+ fcid = tport->fcid;
+ wwpn = tport->wwpn;
+ nexus_restart_count = tport->nexus_restart_count;
+
+ fdls_delete_tport(iport, tport);
+
+ if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Exceeded nexus restart retries tport: 0x%x", fcid);
+ return;
+ }
+
+ /*
+ * Allocate memory for the new tport and add it to
+ * iport's tport list.
+ * This memory will be freed during tport_offline/linkdown
+ * or module unload. The pointer new_tport is safe to go
+ * out of scope when this function returns, since the memory
+ * it is pointing to is guaranteed to be freed later
+ * as mentioned above.
+ */
+ new_tport = fdls_create_tport(iport, fcid, wwpn);
+ if (!new_tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Error creating new tport: 0x%x", fcid);
+ return;
+ }
+
+ new_tport->nexus_restart_count = nexus_restart_count + 1;
+ fdls_send_tgt_plogi(iport, new_tport);
+ fdls_set_tport_state(new_tport, FDLS_TGT_STATE_PLOGI);
+}
+
+struct fnic_tport_s *fnic_find_tport_by_fcid(struct fnic_iport_s *iport,
+ uint32_t fcid)
+{
+ struct fnic_tport_s *tport, *next;
+
+ list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+ if ((tport->fcid == fcid)
+ && !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+ return tport;
+ }
+ return NULL;
+}
+
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+ uint32_t fcid, uint64_t wwpn)
+{
+ struct fnic_tport_s *tport;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);
+
+ tport = kzalloc(sizeof(struct fnic_tport_s), GFP_ATOMIC);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Memory allocation failure while creating tport: 0x%x\n",
+ fcid);
+ return NULL;
+ }
+
+ tport->max_payload_size = FNIC_FCOE_MAX_FRAME_SZ;
+ tport->r_a_tov = FNIC_R_A_TOV_DEF;
+ tport->e_d_tov = FNIC_E_D_TOV_DEF;
+ tport->fcid = fcid;
+ tport->wwpn = wwpn;
+ tport->iport = iport;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Need to setup tport timer callback");
+
+ timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Added tport 0x%x", tport->fcid);
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
+ list_add_tail(&tport->links, &iport->tport_list);
+ atomic_set(&tport->in_flight, 0);
+ return tport;
+}
+
+struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
+ uint64_t wwpn)
+{
+ struct fnic_tport_s *tport, *next;
+
+ list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+ if ((tport->wwpn == wwpn)
+ && !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+ return tport;
+ }
+ return NULL;
+}
+
void fdls_fabric_timer_callback(struct timer_list *t)
{
struct fnic_fdls_fabric_s *fabric = from_timer(fabric, t, retry_timer);
@@ -548,98 +1066,614 @@ void fdls_fabric_timer_callback(struct timer_list *t)
else {
/* ABTS has timed out (2*ra_tov), we give up */
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
- "ABTS timed out. Starting PLOGI: %p", iport);
- fnic_fdls_start_plogi(iport);
+ "ABTS timed out. Starting PLOGI: %p", iport);
+ fnic_fdls_start_plogi(iport);
+ }
+ break;
+ case FDLS_STATE_REGISTER_FC4_TYPES:
+ /* scr received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_register_fc4_types(iport);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ /* RFT_ID timed out send abts */
+ if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ fdls_send_fabric_abts(iport);
+ } else {
+ /* ABTS has timed out (2*ra_tov), we give up */
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ABTS timed out. Starting PLOGI: %p", iport);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FDLS_STATE_REGISTER_FC4_FEATURES:
+ /* scr received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_register_fc4_features(iport);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ /* scr have timed out send abts */
+ if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+ fdls_send_fabric_abts(iport);
+ else {
+ /* ABTS has timed out (2*ra_tov), we give up */
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ABTS timed out. Starting PLOGI %p", iport);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FDLS_STATE_RSCN_GPN_FT:
+ case FDLS_STATE_SEND_GPNFT:
+ case FDLS_STATE_GPN_FT:
+ /* GPN_FT received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_gpn_ft(iport, iport->fabric.state);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ /* gpn_gt have timed out send abts */
+ if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ fdls_send_fabric_abts(iport);
+ } else {
+ /*
+ * ABTS has timed out have waited (2*ra_tov) can
+ * retry safely with same exchange id
+ */
+ if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
+ fdls_send_gpn_ft(iport, iport->fabric.state);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ABTS timeout for fabric GPN_FT. Check name server: %p",
+ iport);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
+{
+ struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+ struct fnic *fnic = iport->fnic;
+ struct fnic_tport_event_s *tport_del_evt;
+
+ if (!IS_FNIC_FCP_INITIATOR(fnic))
+ return;
+
+ tport_del_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+ if (!tport_del_evt) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Failed to allocate memory for tport event fcid: 0x%x",
+ tport->fcid);
+ return;
+ }
+ tport_del_evt->event = TGT_EV_TPORT_DELETE;
+ tport_del_evt->arg1 = (void *) tport;
+ list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+ queue_work(fnic_event_queue, &fnic->tport_work);
+}
+
+static void fdls_tport_timer_callback(struct timer_list *t)
+{
+ struct fnic_tport_s *tport = from_timer(tport, t, retry_timer);
+ struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (!tport->timer_pending) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ if (tport->del_timer_inprogress) {
+ tport->del_timer_inprogress = 0;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n",
+ tport->fcid);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d",
+ tport->fcid, tport->timer_pending, tport->state,
+ tport->retry_counter);
+
+ tport->timer_pending = 0;
+ oxid = ntohs(tport->oxid_used);
+
+ /* We retry plogi/prli/adisc frames depending on the tport state */
+ switch (tport->state) {
+ case FDLS_TGT_STATE_PLOGI:
+ /* PLOGI frame received a LS_RJT with busy, we retry from here */
+ if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+ && (tport->retry_counter < iport->max_plogi_retries)) {
+ fdls_free_tgt_oxid(iport, oxid);
+ tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_tgt_plogi(iport, tport);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ /* Plogi frame has timed out 2*ed_tov send abts */
+ if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ fdls_send_tport_abts(iport, tport);
+ } else if (tport->retry_counter < iport->max_plogi_retries) {
+ /*
+ * ABTS has timed out have waited (2*ra_tov)
+ * can retry safely
+ * even if with same exchange id
+ */
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ } else {
+ /* exceeded plogi retry count */
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_delete_tport_msg(tport);
+ }
+ break;
+ case FDLS_TGT_STATE_PRLI:
+ /* PRLI received a LS_RJT with busy , hence we retry from here */
+ if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ fdls_free_tgt_oxid(iport, oxid);
+ tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_tgt_prli(iport, tport);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ /* PRLI has time out 2*ed_tov send abts */
+ if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ fdls_send_tport_abts(iport, tport);
+ } else {
+ /* ABTS has timed out for prli, we go back to PLOGI */
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+ }
+ break;
+ case FDLS_TGT_STATE_ADISC:
+ /* ADISC timed out send a ABTS */
+ if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ fdls_send_tport_abts(iport, tport);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ } else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ /*
+ * ABTS has timed out have waited (2*ra_tov) can
+ * retry safely even if with same exchange id
+ */
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_tgt_adisc(iport, tport);
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ /* exceeded retry count */
+ fdls_free_tgt_oxid(iport, oxid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ADISC not responding. Deleting target port: 0x%x",
+ tport->fcid);
+ fdls_send_delete_tport_msg(tport);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Unknown tport state: 0x%x", tport->state);
+ break;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
+{
+ iport->fabric.retry_counter = 0;
+ fdls_send_fabric_flogi(iport);
+ fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
+ iport->fabric.flags = 0;
+}
+
+static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
+{
+ iport->fabric.retry_counter = 0;
+ fdls_send_fabric_plogi(iport);
+ fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
+ iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+}
+
+static void
+fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
+ struct fc_hdr_s *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint64_t frame_wwnn;
+ uint64_t frame_wwpn;
+ uint16_t oxid;
+ struct fc_els_adisc_ls_acc_s *adisc_rsp =
+ (struct fc_els_adisc_ls_acc_s *) fchdr;
+ struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
+ struct fnic *fnic = iport->fnic;
+
+ fcid = FNIC_GET_S_ID(fchdr);
+ tgt_fcid = ntoh24(fcid);
+ tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Tgt ADISC response tport not found: 0x%x", tgt_fcid);
+ return;
+ }
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ || (tport->state != FDLS_TGT_STATE_ADISC)
+ || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping this ADISC response");
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "iport state: %d tport state: %d Is abort issued on PRLI? %d",
+ iport->state, tport->state,
+ (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED));
+ return;
+ }
+ if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping frame from target: 0x%x",
+ tgt_fcid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery");
+ return;
+ }
+
+ switch (adisc_rsp->command) {
+ case FC_LS_ACC:
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport 0x%p Canceling fabric disc timer\n",
+ tport);
+ fnic_del_tport_timer_sync();
+ }
+ tport->timer_pending = 0;
+ tport->retry_counter = 0;
+ oxid = ntohs(fchdr->ox_id);
+ fdls_free_tgt_oxid(iport, oxid);
+ frame_wwnn = htonll(adisc_rsp->node_name);
+ frame_wwpn = htonll(adisc_rsp->nport_name);
+ if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ADISC accepted from target: 0x%x. Target logged in",
+ tgt_fcid);
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Error mismatch frame: ADISC");
+ }
+ break;
+
+ case FC_LS_REJ:
+ if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
+ || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ADISC ret FC_LS_REJ BUSY. Retry from timer routine: 0x%x",
+ tgt_fcid);
+
+ /* Retry ADISC again from the timer routine. */
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ADISC returned FC_LS_REJ from target: 0x%x",
+ tgt_fcid);
+ oxid = ntohs(fchdr->ox_id);
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_delete_tport(iport, tport);
+ }
+ break;
+ }
+}
+
+
+static void
+fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
+ struct fc_hdr_s *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint16_t oxid;
+ struct fc_els_s *plogi_rsp = (struct fc_els_s *) fchdr;
+ struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
+ int max_payload_size;
+ struct fnic *fnic = iport->fnic;
+
+ fcid = FNIC_GET_S_ID(fchdr);
+ tgt_fcid = ntoh24(fcid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "FDLS processing target PLOGI response: tgt_fcid: 0x%x",
+ tgt_fcid);
+
+ tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport not found: 0x%x", tgt_fcid);
+ return;
+ }
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping frame! iport state: %d tport state: %d",
+ iport->state, tport->state);
+ return;
+ }
+
+ if (tport->state != FDLS_TGT_STATE_PLOGI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI rsp recvd in wrong state. Restarting nexus");
+ oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_target_restart_nexus(tport);
+ return;
+ }
+
+ if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI response from target: 0x%x. Dropping frame",
+ tgt_fcid);
+ return;
+ }
+
+ switch (plogi_rsp->command) {
+ case FC_LS_ACC:
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI accepted by target: 0x%x", tgt_fcid);
+ oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, oxid);
+ break;
+
+ case FC_LS_REJ:
+ if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
+ || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
+ && (tport->retry_counter < iport->max_plogi_retries)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI ret FC_LS_REJ BUSY. Retry from timer routine: 0x%x",
+ tgt_fcid);
+ /* Retry plogi again from the timer routine. */
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ return;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI returned FC_LS_REJ from target: 0x%x",
+ tgt_fcid);
+ oxid = ntohs(fchdr->ox_id);
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_delete_tport(iport, tport);
+ return;
+
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI not accepted from target fcid: 0x%x",
+ tgt_fcid);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Found the PLOGI target: 0x%x and state: %d",
+ (unsigned int) tgt_fcid, tport->state);
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync();
+ }
+
+ tport->timer_pending = 0;
+ tport->wwpn = htonll(plogi_rsp->nport_name);
+ tport->wwnn = htonll(plogi_rsp->node_name);
+
+ /* Learn the Service Params */
+
+ /* Max frame size - choose the lowest */
+ max_payload_size = fnic_fc_plogi_rsp_rdf(iport, plogi_rsp);
+ tport->max_payload_size =
+ MIN(max_payload_size, iport->max_payload_size);
+
+ if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "MFS: tport max frame size below spec bounds: %d",
+ tport->max_payload_size);
+ tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "MAX frame size: %d iport max_payload_size: %d tport mfs: %d",
+ max_payload_size, iport->max_payload_size,
+ tport->max_payload_size);
+
+ tport->max_concur_seqs = FNIC_FC_PLOGI_RSP_CONCUR_SEQ(plogi_rsp);
+
+ tport->retry_counter = 0;
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PRLI);
+ fdls_send_tgt_prli(iport, tport);
+}
+
+static void
+fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
+ struct fc_hdr_s *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint16_t oxid;
+ struct fc_els_prli_s *prli_rsp = (struct fc_els_prli_s *) fchdr;
+ struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
+ struct fnic_tport_event_s *tport_add_evt;
+ struct fnic *fnic = iport->fnic;
+ bool mismatched_tgt = false;
+
+ fcid = FNIC_GET_S_ID(fchdr);
+ tgt_fcid = ntoh24(fcid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "FDLS process tgt PRLI response: 0x%x", tgt_fcid);
+
+ tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport not found: 0x%x", tgt_fcid);
+ /* Handle or just drop? */
+ return;
+ }
+
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x",
+ iport->state, tport->state, tport->fcid);
+ return;
+ }
+
+ if (tport->state != FDLS_TGT_STATE_PRLI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PRLI rsp recvd in wrong state. Restarting nexus");
+ oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_target_restart_nexus(tport);
+ return;
+ }
+
+ if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping PRLI response from target: 0x%x ",
+ tgt_fcid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery");
+ return;
+ }
+
+ switch (prli_rsp->command) {
+ case FC_LS_ACC:
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PRLI accepted from target: 0x%x", tgt_fcid);
+ oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, oxid);
+
+ if (prli_rsp->sp.type != FC_FC4_TYPE_SCSI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "mismatched target zoned with FC SCSI initiator: 0x%x",
+ tgt_fcid);
+ mismatched_tgt = true;
}
- break;
- case FDLS_STATE_REGISTER_FC4_TYPES:
- /* scr received a LS_RJT with busy we retry from here */
- if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
- && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
- iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
- fdls_send_register_fc4_types(iport);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ if (mismatched_tgt) {
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
return;
}
- /* RFT_ID timed out send abts */
- if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
- fdls_send_fabric_abts(iport);
- } else {
- /* ABTS has timed out (2*ra_tov), we give up */
- FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
- "ABTS timed out. Starting PLOGI: %p", iport);
- fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
- }
break;
- case FDLS_STATE_REGISTER_FC4_FEATURES:
- /* scr received a LS_RJT with busy we retry from here */
- if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
- && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
- iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
- fdls_send_register_fc4_features(iport);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- return;
- }
- /* scr have timed out send abts */
- if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
- fdls_send_fabric_abts(iport);
- else {
- /* ABTS has timed out (2*ra_tov), we give up */
+
+ case FC_LS_REJ:
+ if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
+ || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
- "ABTS timed out. Starting PLOGI %p", iport);
- fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
- }
- break;
- case FDLS_STATE_RSCN_GPN_FT:
- case FDLS_STATE_SEND_GPNFT:
- case FDLS_STATE_GPN_FT:
- /* GPN_FT received a LS_RJT with busy we retry from here */
- if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
- && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
- iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
- fdls_send_gpn_ft(iport, iport->fabric.state);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ "PRLI ret FC_LS_REJ BUSY. Retry from timer routine: 0x%x",
+ tgt_fcid);
+
+ /*Retry Plogi again from the timer routine. */
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
return;
- }
- /* gpn_gt have timed out send abts */
- if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
- fdls_send_fabric_abts(iport);
} else {
- /*
- * ABTS has timed out have waited (2*ra_tov) can
- * retry safely with same exchange id
- */
- if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
- fdls_send_gpn_ft(iport, iport->fabric.state);
- } else {
- FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
- "ABTS timeout for fabric GPN_FT. Check name server: %p",
- iport);
- }
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PRLI returned FC_LS_REJ from target: 0x%x",
+ tgt_fcid);
+
+ oxid = ntohs(fchdr->ox_id);
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
+ return;
}
break;
+
default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PRLI not accepted from target: 0x%x", tgt_fcid);
+ return;
break;
}
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-}

-static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
-{
- iport->fabric.retry_counter = 0;
- fdls_send_fabric_flogi(iport);
- fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
- iport->fabric.flags = 0;
-}
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Found the PRLI target: 0x%x and state: %d",
+ (unsigned int) tgt_fcid, tport->state);

-static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
-{
- iport->fabric.retry_counter = 0;
- fdls_send_fabric_plogi(iport);
- fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
- iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync();
+ }
+ tport->timer_pending = 0;
+
+ oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, oxid);
+
+ /* Learn Service Params */
+ tport->fcp_csp = ntohl(prli_rsp->sp.csp);
+ tport->retry_counter = 0;
+
+ if (prli_rsp->sp.csp & FCP_SPPF_RETRY)
+ tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
+
+ /* Check if the device plays Target Mode Function */
+ if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Remote port(0x%x): no target support. Deleting it\n",
+ tgt_fcid);
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
+ return;
+ }
+
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+
+ /* Inform the driver about new target added */
+ tport_add_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+ if (!tport_add_evt) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport event memory allocation failure: 0x%0x\n",
+ tport->fcid);
+ return;
+ }
+ tport_add_evt->event = TGT_EV_RPORT_ADD;
+ tport_add_evt->arg1 = (void *) tport;
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "iport fcid: 0x%x add tport event fcid: 0x%x\n",
+ tport->fcid, iport->fcid);
+ list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
+ queue_work(fnic_event_queue, &fnic->tport_work);
}

+
static void
fdls_process_rff_id_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr)
{
@@ -884,6 +1918,106 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr)
}
}

+static void
+fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
+ struct fc_hdr_s *fchdr, int len)
+{
+ struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt;
+ struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt_rem;
+ struct fnic_tport_s *tport, *next;
+ uint32_t fcid;
+ uint64_t wwpn;
+ int rem_len = len;
+ u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
+
+ gpn_ft_tgt =
+ (struct fc_gpn_ft_rsp_iu_s *) ((uint8_t *) fchdr +
+ sizeof(struct fc_hdr_s)
+ + sizeof(struct fc_ct_hdr_s));
+ gpn_ft_tgt_rem = gpn_ft_tgt;
+ len -= sizeof(struct fc_hdr_s) + sizeof(struct fc_ct_hdr_s);
+
+ while (rem_len > 0) {
+
+ fcid = ntoh24(gpn_ft_tgt->fcid);
+ wwpn = ntohll(gpn_ft_tgt->wwpn);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
+
+ if (fcid == iport->fcid) {
+ if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
+ break;
+ gpn_ft_tgt++;
+ rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
+ continue;
+ }
+
+ tport = fnic_find_tport_by_wwpn(iport, wwpn);
+ if (!tport) {
+ /*
+ * New port registered with the switch or first time query
+ */
+ tport = fdls_create_tport(iport, fcid, wwpn);
+ if (!tport)
+ return;
+ }
+ /*
+ * check if this was an existing tport with same fcid
+ * but whose wwpn has changed now ,then remove it and
+ * create a new one
+ */
+ if (tport->fcid != fcid) {
+ fdls_delete_tport(iport, tport);
+ tport = fdls_create_tport(iport, fcid, wwpn);
+ if (!tport)
+ return;
+ }
+
+ /*
+ * If this GPN_FT rsp is after RSCN then mark the tports which
+ * matches with the new GPN_FT list, if some tport is not
+ * found in GPN_FT we went to delete that tport later.
+ */
+ if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT)
+ tport->flags |= FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+
+ if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
+ break;
+
+ gpn_ft_tgt++;
+ rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
+ }
+ if (rem_len <= 0) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
+ len, rem_len);
+ }
+
+ /*remove those ports which was not listed in GPN_FT */
+ if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT) {
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+
+ if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Remove port: 0x%x not found in GPN_FT list",
+ tport->fcid);
+ fdls_delete_tport(iport, tport);
+ } else {
+ tport->flags &= ~FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+ }
+ if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+ || (iport->state != FNIC_IPORT_STATE_READY)) {
+ return;
+ }
+ }
+ }
+}
+
static void
fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
int len)
@@ -892,6 +2026,9 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
struct fc_gpn_ft_s *gpn_ft_rsp = (struct fc_gpn_ft_s *) fchdr;
uint16_t rsp;
uint8_t reason_code;
+ int count = 0;
+ struct fnic_tport_s *tport, *next;
+ u32 old_link_down_cnt = iport->fnic->link_down_cnt;
struct fnic *fnic = iport->fnic;

FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
@@ -928,12 +2065,74 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
case FC_CT_ACC:
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
"0x%x: GPNFT_RSP accept", iport->fcid);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: Canceling fabric disc timer\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync();
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ fdls_process_gpn_ft_tgt_list(iport, fchdr, len);
+
+ /*
+ * iport state can change only if link down event happened
+ * We don't need to undo fdls_process_gpn_ft_tgt_list,
+ * that will be taken care in next link up event
+ */
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Halting target discovery: fab st: %d iport st: %d ",
+ fdls_get_state(fdls), iport->state);
+ break;
+ }
+ fdls_tgt_discovery_start(iport);
break;

case FC_CT_REJ:
reason_code = gpn_ft_rsp->fc_ct_hdr.reason_code;
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
"0x%x: GPNFT_RSP Reject", iport->fcid);
+
+ if (((reason_code == FC_CT_RJT_LOGICAL_BUSY)
+ || (reason_code == FC_CT_RJT_BUSY))
+ && (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine",
+ iport->fcid);
+ /* Retry again from the timer routine */
+ fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: GPNFT_RSP reject", iport->fcid);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: Canceling fabric disc timer\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync();
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ /*
+ * If GPN_FT ls_rjt then we should delete
+ * all existing tports
+ */
+ count = 0;
+ list_for_each_entry_safe(tport, next, &iport->tport_list,
+ links) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "GPN_FT_REJECT: Remove port: 0x%x",
+ tport->fcid);
+ fdls_delete_tport(iport, tport);
+ if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+ || (iport->state != FNIC_IPORT_STATE_READY)) {
+ return;
+ }
+ count++;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "GPN_FT_REJECT: Removed (0x%x) ports", count);
+ }
break;

default:
@@ -1342,6 +2541,141 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
}
}

+static void
+fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
+ struct fc_hdr_s *fchdr)
+{
+ uint32_t s_id;
+ struct fnic_tport_s *tport;
+ uint32_t tport_state;
+ struct fc_abts_ba_acc_s *ba_acc;
+ struct fc_abts_ba_rjt_s *ba_rjt;
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+
+ s_id = ntoh24(fchdr->sid);
+ ba_acc = (struct fc_abts_ba_acc_s *) fchdr;
+ ba_rjt = (struct fc_abts_ba_rjt_s *) fchdr;
+
+ tport = fnic_find_tport_by_fcid(iport, s_id);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "Received tgt abts rsp with invalid SID: 0x%x", s_id);
+ return;
+ }
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "tport 0x%p Canceling fabric disc timer\n", tport);
+ fnic_del_tport_timer_sync();
+ }
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "Received tgt abts rsp in iport state(%d). Dropping.",
+ iport->state);
+ return;
+ }
+ tport->timer_pending = 0;
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+ tport_state = tport->state;
+ oxid = ntohs(fchdr->ox_id);
+
+ /*This abort rsp is for ADISC */
+ if ((oxid >= FDLS_ADISC_OXID_BASE) && (oxid < FDLS_TGT_OXID_POOL_END)) {
+ if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "Received tgt ADISC abts response BA_ACC for OX_ID: 0x%x tgt_fcid: 0x%x",
+ ba_acc->ox_id, tport->fcid);
+ } else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ",
+ tport->fcid, tport_state);
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation:0x%x ",
+ ba_rjt->reason_code, ba_rjt->reason_explanation);
+ }
+ if ((tport->retry_counter < FDLS_RETRY_COUNT)
+ && (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_tgt_adisc(iport, tport);
+ return;
+ }
+ fdls_free_tgt_oxid(iport, oxid);
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "ADISC not responding. Deleting target port: 0x%x",
+ tport->fcid);
+ fdls_delete_tport(iport, tport);
+ if ((iport->state == FNIC_IPORT_STATE_READY)
+ && (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+ && (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+ fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+ }
+ /*Restart a discovery of targets */
+ return;
+ }
+
+ /*This abort rsp is for PLOGI */
+ if ((oxid >= FDLS_PLOGI_OXID_BASE) && (oxid < FDLS_PRLI_OXID_BASE)) {
+ if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x",
+ tport->fcid);
+ } else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x",
+ tport->fcid, fchdr->ox_id);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation: 0x%x",
+ ba_rjt->reason_code, ba_rjt->reason_explanation);
+ }
+ if ((tport->retry_counter < iport->max_plogi_retries)
+ && (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ return;
+ }
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_delete_tport(iport, tport);
+ /*Restart a discovery of targets */
+ if ((iport->state == FNIC_IPORT_STATE_READY)
+ && (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+ && (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+ fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+ }
+ return;
+ }
+
+ /*This abort rsp is for PRLI */
+ if ((oxid >= FDLS_PRLI_OXID_BASE) && (oxid < FDLS_ADISC_OXID_BASE)) {
+ if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "0x%x: Received tgt PRLI abts response BA_ACC",
+ tport->fcid);
+ } else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ",
+ tport->fcid, fchdr->ox_id);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation: 0x%x",
+ ba_rjt->reason_code,
+ ba_rjt->reason_explanation);
+ }
+ if ((tport->retry_counter < FDLS_RETRY_COUNT)
+ && (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_tgt_prli(iport, tport);
+ return;
+ }
+ fdls_free_tgt_oxid(iport, oxid);
+ fdls_send_tgt_plogi(iport, tport); /* go back to plogi */
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Received ABTS response for unknown frame %p", iport);
+}
+
/*
* Performs a validation for all FCOE frames and return the frame type
*/
@@ -1434,6 +2768,42 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
}
}

+ /* ELS response from a target */
+ if ((ntohs(oxid) >= FDLS_PLOGI_OXID_BASE)
+ && (ntohs(oxid) < FDLS_PRLI_OXID_BASE)) {
+ if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping Unknown frame in PLOGI exchange range type: 0x%x.",
+ fchdr->type);
+ return -1;
+ }
+ return FNIC_TPORT_PLOGI_RSP;
+ }
+ if ((ntohs(oxid) >= FDLS_PRLI_OXID_BASE)
+ && (ntohs(oxid) < FDLS_ADISC_OXID_BASE)) {
+ if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping Unknown frame in PRLI exchange range type: 0x%x.",
+ fchdr->type);
+ return -1;
+ }
+ return FNIC_TPORT_PRLI_RSP;
+ }
+
+ if ((ntohs(oxid) >= FDLS_ADISC_OXID_BASE)
+ && (ntohs(oxid) < FDLS_TGT_OXID_POOL_END)) {
+ if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Dropping Unknown frame in ADISC exchange range type: 0x%x.",
+ fchdr->type);
+ return -1;
+ }
+ return FNIC_TPORT_ADISC_RSP;
+ }
+ if (ntohs(oxid) == FNIC_TLOGO_REQ_OXID) {
+ return FNIC_TPORT_LOGO_RSP;
+ }
+
/*response from fabric */
switch (oxid) {

@@ -1557,6 +2927,21 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
case FNIC_FABRIC_GPN_FT_RSP:
fdls_process_gpn_ft_rsp(iport, fchdr, len);
break;
+ case FNIC_TPORT_PLOGI_RSP:
+ fdls_process_tgt_plogi_rsp(iport, fchdr);
+ break;
+ case FNIC_TPORT_PRLI_RSP:
+ fdls_process_tgt_prli_rsp(iport, fchdr);
+ break;
+ case FNIC_TPORT_ADISC_RSP:
+ fdls_process_tgt_adisc_rsp(iport, fchdr);
+ break;
+ case FNIC_TPORT_LOGO_RSP:
+ /* Logo response from tgt which we have deleted */
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Logo response from tgt: 0x%x",
+ ntoh24(fchdr->sid));
+ break;
case FNIC_FABRIC_LOGO_RSP:
fdls_process_fabric_logo_rsp(iport, fchdr);
break;
@@ -1566,7 +2951,8 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
if ((iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)
&& (oxid >= FNIC_FLOGI_OXID && oxid <= FNIC_RFF_REQ_OXID)) {
fdls_process_fabric_abts_rsp(iport, fchdr);
- }
+ } else
+ fdls_process_tgt_abts_rsp(iport, fchdr);
break;
default:
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 2d5f438f2cc4..92cd17efa40f 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -79,6 +79,9 @@

#define IS_FNIC_FCP_INITIATOR(fnic) (fnic->role == FNIC_ROLE_FCP_INITIATOR)

+/* Retry supported by rport (returned by PRLI service parameters) */
+#define FNIC_FC_RP_FLAGS_RETRY 0x1
+
/*
* fnic private data per SCSI command.
* These fields are locked by the hashed io_req_lock.
@@ -133,6 +136,7 @@ static inline u64 fnic_flags_and_state(struct scsi_cmnd *cmd)

extern unsigned int fnic_log_level;
extern unsigned int io_completions;
+extern struct workqueue_struct *fnic_event_queue;

#define FNIC_MAIN_LOGGING 0x01
#define FNIC_FCS_LOGGING 0x02
@@ -329,6 +333,8 @@ struct fnic {
struct work_struct flush_work;
struct sk_buff_head frame_queue;
struct list_head tx_queue;
+ struct work_struct tport_work;
+ struct list_head tport_event_list;

/*** FIP related data members -- start ***/
void (*set_vlan)(struct fnic *, u16 vlan);
diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
index 095275698716..9eb25ed9c19f 100644
--- a/drivers/scsi/fnic/fnic_fdls.h
+++ b/drivers/scsi/fnic/fnic_fdls.h
@@ -325,6 +325,10 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport);
int fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
void *rx_frame, int len,
int fchdr_offset);
+void fdls_send_tport_abts(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport);
+void fdls_delete_tport(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport);

/* fnic_fcs.c */
void fnic_fdls_init(struct fnic *fnic, int usefip);
--
2.31.1