Add support for target based solicited requests and responses.
Add support for tport definitions and processing.
Add support for restarting the IT nexus.
Reported-by: kernel test robot <lkp@intel.com>
Closes:
https://lore.kernel.org/oe-kbuild-all/202406120146.xchlZbqX-lkp@
intel.com/
Reviewed-by: Sesidhar Baddela <sebaddel@cisco.com>
Co-developed-by: Gian Carlo Boffa <gcboffa@cisco.com>
Signed-off-by: Gian Carlo Boffa <gcboffa@cisco.com>
Co-developed-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Signed-off-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Co-developed-by: Arun Easi <aeasi@cisco.com>
Signed-off-by: Arun Easi <aeasi@cisco.com>
Co-developed-by: Karan Tilak Kumar <kartilak@cisco.com>
Signed-off-by: Karan Tilak Kumar <kartilak@cisco.com>
---
Changes between v4 and v5:
Incorporate review comments from Martin:
Call fdls_get_tgt_oxid_pool.
Modify attribution appropriately.
Changes between v2 and v3:
Fix issue found by kernel test robot.
Remove fnic_std_ba_acc definition to fix compilation
warning.
Incorporate review comments from Hannes:
Replace redundant definitions with standard definitions.
Replace static OXIDs with pool-based OXIDs for targets.
Changes between v1 and v2:
Incorporate review comments from Hannes:
Use the correct kernel-doc format.
Replace htonll() with get_unaligned_be64().
Replace fnic_del_fabric_timer_sync macro calls to function
calls.
Replace fnic_del_tport_timer_sync macro calls to function
calls.
Rename fc_abts_s to fc_tport_abts_s.
Modify fc_tport_abts_s to be a global frame.
Rename variable pfc_abts to tport_abts.
Replace definitions with standard definitions from
fc_els.h.
Modify functions with returns in the middle to if else
clauses.
Replace simultaneous use of fc_tport_abts_s and tport_abts with
just tport_abts.
---
drivers/scsi/fnic/fdls_disc.c | 1569 +++++++++++++++++++++++++++++++--
drivers/scsi/fnic/fnic.h | 6 +
drivers/scsi/fnic/fnic_fdls.h | 2 +-
3 files changed, 1502 insertions(+), 75 deletions(-)
diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index 3a995b76a864..52459f6bb589 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -11,6 +11,10 @@
#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 */
/*
* Variables:
@@ -66,6 +70,20 @@ struct fc_std_rpn_id fnic_std_rpn_id_req = {
.ct_cmd = cpu_to_be16(FC_NS_RPN_ID)}
};
+/*
+ * Variables:
+ * did, sid, oxid
+ */
+struct fc_std_els_prli fnic_std_prli_req = {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}, .fh_rx_id = 0xFFFF},
+ .els_prli = {.prli_cmd = ELS_PRLI,
+ .prli_spp_len = 16,
+ .prli_len = cpu_to_be16(0x14)},
+ .sp = {.spp_type = 0x08, .spp_flags = 0x0020,
+ .spp_params = cpu_to_be32(0xA2)}
+};
+
/*
* Variables:
* fh_s_id, port_id, port_name
@@ -143,9 +161,19 @@ struct fc_frame_header fc_std_fabric_abts = {
.fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */
};
+struct fc_frame_header fc_std_tport_abts = {
+ .fh_r_ctl = FC_RCTL_BA_ABTS, /* ABTS */
+ .fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS,
+ .fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00,
+ .fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000, .fh_rx_id = 0xFFFF,
+ .fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */
+};
+
#define RETRIES_EXHAUSTED(iport) \
(iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)
+#define FNIC_TPORT_MAX_NEXUS_RESTART (8)
+
/*
* For fabric requests and fdmi, once OXIDs are allocated from the pool
* (and a range) they are encoded with expected rsp type as
@@ -167,6 +195,14 @@ static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
void *rx_frame);
static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
static void fnic_fdls_start_flogi(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_tport_timer_callback(struct timer_list *t);
+
static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
int timeout);
static void
@@ -182,6 +218,8 @@ void fdls_init_oxid_pool(struct fnic_iport_s *iport)
fdls_init_fabric_oxid_pool(&iport->fdmi_oxid_pool,
FDLS_FDMI_OXID_POOL_BASE,
FDLS_FDMI_OXID_POOL_SZ);
+
+ fdls_init_tgt_oxid_pool(iport);
}
uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport,
@@ -313,6 +351,13 @@ static inline void fdls_schedule_fabric_oxid_free(struct fnic_iport_s
iport->fabric_oxid_pool.active_oxid_fabric_req);
}
+static inline void fdls_schedule_tgt_oxid_free(struct fnic_iport_s *iport,
+ struct fnic_tgt_oxid_pool_s
+ *oxid_pool, uint16_t oxid)
+{
+ fdls_schedule_oxid_free(&oxid_pool->meta, oxid);
+}
+
int fnic_fdls_expected_rsp(struct fnic_iport_s *iport, uint16_t oxid)
{
struct fnic *fnic = iport->fnic;
@@ -340,6 +385,62 @@ static int fdls_is_oxid_in_fabric_range(uint16_t oxid)
(oxid_unmasked <= FDLS_FABRIC_OXID_POOL_END));
}
+void fdls_init_tgt_oxid_pool(struct fnic_iport_s *iport)
+{
+ memset(&iport->plogi_oxid_pool, 0, sizeof(iport->plogi_oxid_pool));
+ iport->plogi_oxid_pool.meta.oxid_base = FDLS_PLOGI_OXID_BASE;
+ iport->plogi_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
+ INIT_LIST_HEAD(&iport->plogi_oxid_pool.meta.reclaim_list);
+
+ memset(&iport->prli_oxid_pool, 0, sizeof(iport->prli_oxid_pool));
+ iport->prli_oxid_pool.meta.oxid_base = FDLS_PRLI_OXID_BASE;
+ iport->prli_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
+ INIT_LIST_HEAD(&iport->prli_oxid_pool.meta.reclaim_list);
+
+ memset(&iport->adisc_oxid_pool, 0, sizeof(iport->adisc_oxid_pool));
+ iport->adisc_oxid_pool.meta.oxid_base = FDLS_ADISC_OXID_BASE;
+ iport->adisc_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
+ INIT_LIST_HEAD(&iport->adisc_oxid_pool.meta.reclaim_list);
+}
+
+inline uint16_t fdls_alloc_tgt_oxid(struct fnic_iport_s *iport,
+ struct fnic_tgt_oxid_pool_s *oxid_pool)
+{
+ uint16_t oxid;
+
+ oxid = fdls_alloc_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap);
+ return oxid;
+}
+
+inline void fdls_free_tgt_oxid(struct fnic_iport_s *iport,
+ struct fnic_tgt_oxid_pool_s *oxid_pool,
+ uint16_t oxid)
+{
+ fdls_free_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap, oxid);
+}
+
+static struct fnic_tgt_oxid_pool_s *fdls_get_tgt_oxid_pool(struct fnic_tport_s
+ *tport)
+{
+ struct fnic_iport_s *iport = (struct fnic_iport_s *)tport->iport;
+ struct fnic_tgt_oxid_pool_s *oxid_pool = NULL;
+
+ switch (tport->state) {
+ case FDLS_TGT_STATE_PLOGI:
+ oxid_pool = &iport->plogi_oxid_pool;
+ break;
+ case FDLS_TGT_STATE_PRLI:
+ oxid_pool = &iport->prli_oxid_pool;
+ break;
+ case FDLS_TGT_STATE_ADISC:
+ oxid_pool = &iport->adisc_oxid_pool;
+ break;
+ default:
+ break;
+ }
+ return oxid_pool;
+}
+
inline void fnic_del_fabric_timer_sync(struct fnic *fnic)
{
fnic->iport.fabric.del_timer_inprogress = 1;
@@ -383,6 +484,56 @@ 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(fnic, tport);
+ 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_frame_header tport_abort = fc_std_tport_abts;
+ struct fc_frame_header *tport_abts = &tport_abort;
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+ FNIC_STD_SET_S_ID(tport_abts, s_id);
+ FNIC_STD_SET_D_ID(tport_abts, d_id);
+ tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ tport_abts->fh_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, tport_abts, sizeof(struct fc_frame_header));
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+
static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
{
uint8_t fcid[3];
@@ -614,6 +765,176 @@ 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_std_els_adisc 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_std_els_adisc));
+ FNIC_STD_SET_R_CTL(&adisc.fchdr, 0x22);
+ FNIC_STD_SET_TYPE(&adisc.fchdr, 0x01);
+ FNIC_STD_SET_F_CTL(&adisc.fchdr, FNIC_ELS_REQ_FCTL << 16);
+ FNIC_STD_SET_RX_ID(&adisc.fchdr, cpu_to_be16(0xFFFF));
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+ FNIC_STD_SET_S_ID(&adisc.fchdr, s_id);
+ FNIC_STD_SET_D_ID(&adisc.fchdr, d_id);
+
+ oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->adisc_oxid_pool));
+ 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_STD_SET_OX_ID((&adisc.fchdr), oxid);
+ FNIC_STD_SET_NPORT_NAME(&adisc.els.adisc_wwpn,
+ le64_to_cpu(iport->wwpn));
+ FNIC_STD_SET_NODE_NAME(&adisc.els.adisc_wwnn, le64_to_cpu(iport->wwnn));
+
+ memcpy(adisc.els.adisc_port_id, s_id, 3);
+ adisc.els.adisc_cmd = ELS_ADISC;
+
+ 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_std_els_adisc));
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+
+bool 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)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "tport fcid 0x%x: tport state is offlining/offline\n",
+ tport->fcid);
+ return false;
+ }
+
+ 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(fnic, tport);
+ 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 false;
+ }
+ 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);
+ }
+ }
+ return true;
+}
+
+static void
+fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ struct fc_std_flogi 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_std_plogi_req, sizeof(struct fc_std_flogi));
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+
+ FNIC_STD_SET_S_ID(&plogi.fchdr, s_id);
+ FNIC_STD_SET_D_ID(&plogi.fchdr, d_id);
+ FNIC_LOGI_SET_RDF_SIZE(&plogi.els, iport->max_payload_size);
+
+ oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool));
+ 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_STD_SET_OX_ID((&plogi.fchdr), oxid);
+ FNIC_LOGI_SET_NPORT_NAME(&plogi.els, iport->wwpn);
+ FNIC_LOGI_SET_NODE_NAME(&plogi.els, iport->wwnn);
+
+ timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+
+ fnic_send_fcoe_frame(iport, &plogi, sizeof(struct fc_std_flogi));
+ /* 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_std_flogi *plogi_rsp)
+{
+ uint16_t b2b_rdf_size =
+ be16_to_cpu(FNIC_LOGI_RDF_SIZE(&plogi_rsp->els));
+ uint16_t spc3_rdf_size =
+ be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & 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_std_rft_id rft_id;
@@ -690,6 +1011,48 @@ 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_std_els_prli 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, &iport->prli_oxid_pool));
+ 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_std_prli_req, sizeof(struct fc_std_els_prli));
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+
+ FNIC_STD_SET_S_ID((&prli.fchdr), s_id);
+ FNIC_STD_SET_D_ID((&prli.fchdr), d_id);
+ FNIC_STD_SET_OX_ID((&prli.fchdr), oxid);
+
+ timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+ fdls_get_tgt_oxid_pool(tport);
+ fnic_send_fcoe_frame(iport, &prli, sizeof(struct fc_std_els_prli));
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, timeout);
+}
+
/**
* fdls_send_fabric_logo - Send flogo to the fcf
* @iport: Handle to fnic iport
@@ -742,6 +1105,212 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport)
fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo));
}
+/**
+ * fdls_tgt_logout - Send plogo to the remote port
+ * @iport: Handle to fnic iport
+ * @tport: Handle to remote port
+ *
+ * This function does not change or check the fabric/tport state.
+ * It the caller's responsibility to set the appropriate tport/fabric
+ * state when this is called. Normally that is fdls_tgt_state_plogo.
+ * 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_std_logo logo;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "Sending logo to tport fcid: 0x%x", tport->fcid);
+ memcpy(&logo, &fnic_std_logo_req, sizeof(struct fc_std_logo));
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+
+ FNIC_STD_SET_S_ID((&logo.fchdr), s_id);
+ FNIC_STD_SET_D_ID((&logo.fchdr), d_id);
+
+ oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool));
+ FNIC_STD_SET_OX_ID((&logo.fchdr), oxid);
+
+ memcpy(&logo.els.fl_n_port_id, s_id, 3);
+ FNIC_STD_SET_NPORT_NAME(&logo.els.fl_n_port_wwn,
+ le64_to_cpu(iport->wwpn));
+
+
+ fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo));
+}
+
+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;
+ bool retval = true;
+
+ 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;
+
+ retval = fdls_delete_tport(iport, tport);
+ if (retval != true) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "Error deleting tport: 0x%x", fcid);
+ return;
+ }
+
+ 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);
@@ -847,90 +1416,588 @@ void fdls_fabric_timer_callback(struct timer_list *t)
/* ABTS has timed out */
fdls_schedule_fabric_oxid_free(iport);
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);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ /* RFT_ID timed out send abts */
+ fdls_send_fabric_abts(iport);
+ } else {
+ /* ABTS has timed out */
+ fdls_schedule_fabric_oxid_free(iport);
+ 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);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+ /* SCR has timed out. Send abts */
+ fdls_send_fabric_abts(iport);
+ else {
+ /* ABTS has timed out */
+ fdls_schedule_fabric_oxid_free(iport);
+ 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);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ /* gpn_ft has timed out. Send abts */
+ fdls_send_fabric_abts(iport);
+ } else {
+ /* ABTS has timed out */
+ fdls_schedule_fabric_oxid_free(iport);
+ 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:
+ fnic_fdls_start_flogi(iport); /* Placeholder call */
+ 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)) {
+ tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_tgt_plogi(iport, tport);
+ } else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ /* Plogi frame has timed out, send abts */
+ fdls_send_tport_abts(iport, tport);
+ } else if (tport->retry_counter < iport->max_plogi_retries) {
+ /*
+ * ABTS has timed out
+ */
+ fdls_schedule_tgt_oxid_free(iport,
+ &iport->plogi_oxid_pool,
+ oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ } else {
+ /* exceeded plogi retry count */
+ fdls_schedule_tgt_oxid_free(iport,
+ &iport->plogi_oxid_pool,
+ 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)) {
+ tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_tgt_prli(iport, tport);
+ } else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ /* PRLI has time out, send abts */
+ fdls_send_tport_abts(iport, tport);
+ } else {
+ /* ABTS has timed out for prli, we go back to PLOGI */
+ fdls_schedule_tgt_oxid_free(iport,
+ &iport->prli_oxid_pool,
+ 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 an ABTS */
+ if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ fdls_send_tport_abts(iport, tport);
+ } else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ /*
+ * ABTS has timed out
+ */
+ fdls_schedule_tgt_oxid_free(iport,
+ &iport->adisc_oxid_pool,
+ oxid);
+ fdls_send_tgt_adisc(iport, tport);
+ } else {
+ /* exceeded retry count */
+ fdls_schedule_tgt_oxid_free(iport,
+ &iport->adisc_oxid_pool,
+ 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);
+ }
+ break;
+ 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_frame_header *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_std_els_adisc *adisc_rsp = (struct fc_std_els_adisc *)fchdr;
+ struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
+ struct fnic *fnic = iport->fnic;
+
+ fcid = FNIC_STD_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->fh_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;
+ }
+
+ oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, oxid);
+
+ switch (adisc_rsp->els.adisc_cmd) {
+ case ELS_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(fnic, tport);
+ }
+ tport->timer_pending = 0;
+ tport->retry_counter = 0;
+ frame_wwnn = get_unaligned_be64(&adisc_rsp->els.adisc_wwnn);
+ frame_wwpn = get_unaligned_be64(&adisc_rsp->els.adisc_wwpn);
+ 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 ELS_LS_RJT:
+ if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "ADISC ret ELS_LS_RJT 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 ELS_LS_RJT from target: 0x%x",
+ tgt_fcid);
+ fdls_delete_tport(iport, tport);
+ }
+ break;
+ }
+}
+
+
+static void
+fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint16_t oxid;
+ struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *)fchdr;
+ struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
+ int max_payload_size;
+ struct fnic *fnic = iport->fnic;
+
+ fcid = FNIC_STD_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. Drop the frame and restart nexus");
+ fdls_target_restart_nexus(tport);
+ return;
+ }
+
+ if (fchdr->fh_ox_id != 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;
+ }
+
+ oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, oxid);
+
+ switch (plogi_rsp->els.fl_cmd) {
+ case ELS_LS_ACC:
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI accepted by target: 0x%x", tgt_fcid);
+ break;
+
+ case ELS_LS_RJT:
+ if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
+ && (tport->retry_counter < iport->max_plogi_retries)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PLOGI ret ELS_LS_RJT 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 ELS_LS_RJT from target: 0x%x",
+ tgt_fcid);
+ 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(fnic, tport);
+ }
+
+ tport->timer_pending = 0;
+ tport->wwpn = get_unaligned_be64(&FNIC_LOGI_PORT_NAME(&plogi_rsp->els));
+ tport->wwnn = get_unaligned_be64(&FNIC_LOGI_NODE_NAME(&plogi_rsp->els));
+
+ /* 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_frame_header *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint16_t oxid;
+ struct fc_std_els_prli *prli_rsp = (struct fc_std_els_prli *)fchdr;
+ struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
+ struct fnic_tport_event_s *tport_add_evt;
+ struct fnic *fnic = iport->fnic;
+ bool mismatched_tgt = false;
+
+ fcid = FNIC_STD_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. Drop frame. Restarting nexus");
+ fdls_target_restart_nexus(tport);
+ return;
+ }
+
+ if (fchdr->fh_ox_id != 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;
+ }
+
+ oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
+ fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
+
+ switch (prli_rsp->els_prli.prli_cmd) {
+ case ELS_LS_ACC:
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "PRLI accepted from target: 0x%x", tgt_fcid);
+
+ if (prli_rsp->sp.spp_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);
- } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
- /* RFT_ID timed out send abts */
- fdls_send_fabric_abts(iport);
- } else {
- /* ABTS has timed out */
- fdls_schedule_fabric_oxid_free(iport);
- 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 */
+ if (mismatched_tgt) {
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
+ return;
}
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);
- } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
- /* SCR has timed out. Send abts */
- fdls_send_fabric_abts(iport);
- else {
- /* ABTS has timed out */
- fdls_schedule_fabric_oxid_free(iport);
+ case ELS_LS_RJT:
+ if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
+ && (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);
- } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
- /* gpn_ft has timed out. Send abts */
- fdls_send_fabric_abts(iport);
+ "PRLI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
+ tgt_fcid);
+
+ /*Retry Plogi again from the timer routine. */
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ return;
} else {
- /* ABTS has timed out */
- fdls_schedule_fabric_oxid_free(iport);
- 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 ELS_LS_RJT from target: 0x%x",
+ tgt_fcid);
+
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
+ return;
}
break;
+
default:
- fnic_fdls_start_flogi(iport); /* Placeholder call */
+ 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(fnic, tport);
+ }
+ tport->timer_pending = 0;
+
+ /* Learn Service Params */
+ tport->fcp_csp = be32_to_cpu(prli_rsp->sp.spp_params);
+ tport->retry_counter = 0;
+
+ if (prli_rsp->sp.spp_params & 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_frame_header *fchdr)
@@ -1179,7 +2246,8 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport,
iport);
if (iport->fabric.timer_pending) {
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
- "Canceling fabric disc timer %p\n", iport);
+ "Canceling fabric disc timer %p\n",
+ iport);
fnic_del_fabric_timer_sync(fnic);
}
fdls->timer_pending = 0;
@@ -1192,6 +2260,104 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport,
}
}
+static void
+fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr, int len)
+{
+ struct fc_gpn_ft_rsp_iu *gpn_ft_tgt;
+ 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 *)((uint8_t *) fchdr +
+ sizeof(struct fc_frame_header)
+ + sizeof(struct fc_ct_hdr));
+ len -= sizeof(struct fc_frame_header) + sizeof(struct fc_ct_hdr);
+
+ 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);
+ 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);
+ }
+ 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_frame_header *fchdr, int len)
@@ -1200,6 +2366,9 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
struct fc_std_gpn_ft *gpn_ft_rsp = (struct fc_std_gpn_ft *) 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,
@@ -1239,12 +2408,74 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
case FC_FS_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(fnic);
+ }
+ 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_FS_RJT:
reason_code = gpn_ft_rsp->fc_std_ct_hdr.ct_reason;
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
"0x%x: GPNFT_RSP Reject reason: %d", iport->fcid, reason_code);
+
+ if (((reason_code == FC_FS_RJT_BSY)
+ || (reason_code == FC_FS_RJT_UNABL))
+ && (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(fnic);
+ }
+ 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:
@@ -1494,8 +2725,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
struct fc_frame_header *fchdr)
{
uint32_t s_id;
- struct fc_std_abts_ba_acc *ba_acc =
- (struct fc_std_abts_ba_acc *) fchdr;
+ struct fc_std_abts_ba_acc *ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
struct fc_std_abts_ba_rjt *ba_rjt;
uint32_t fabric_state = iport->fabric.state;
struct fnic *fnic = iport->fnic;
@@ -1660,6 +2890,148 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
}
}
+static void
+fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t s_id;
+ struct fnic_tport_s *tport;
+ uint32_t tport_state;
+ struct fc_std_abts_ba_acc *ba_acc;
+ struct fc_std_abts_ba_rjt *ba_rjt;
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+
+ s_id = ntoh24(fchdr->fh_s_id);
+ ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
+ ba_rjt = (struct fc_std_abts_ba_rjt *)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(fnic, tport);
+ }
+ 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->fh_ox_id);
+
+ /*This abort rsp is for ADISC */
+ if ((oxid >= FDLS_ADISC_OXID_BASE) && (oxid < FDLS_TGT_OXID_POOL_END)) {
+ if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+ "OX_ID: 0x%x tgt_fcid: 0x%x rcvd tgt adisc abts resp BA_ACC",
+ be16_to_cpu(ba_acc->acc.ba_ox_id),
+ tport->fcid);
+ } else if (fchdr->fh_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->rjt.br_reason,
+ ba_rjt->rjt.br_explan);
+ }
+ if ((tport->retry_counter < FDLS_RETRY_COUNT)
+ && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
+ fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool,
+ oxid);
+ fdls_send_tgt_adisc(iport, tport);
+ return;
+ }
+
+ fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, 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->fh_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->fh_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->fh_ox_id);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation: 0x%x",
+ ba_rjt->rjt.br_reason,
+ ba_rjt->rjt.br_explan);
+ }
+ if ((tport->retry_counter < iport->max_plogi_retries)
+ && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
+ fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool,
+ oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ return;
+ }
+
+ fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, 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->fh_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->fh_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->fh_ox_id);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation: 0x%x",
+ ba_rjt->rjt.br_reason,
+ ba_rjt->rjt.br_explan);
+ }
+ if ((tport->retry_counter < FDLS_RETRY_COUNT)
+ && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
+ fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
+ fdls_send_tgt_prli(iport, tport);
+ return;
+ }
+ fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, 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
*/
@@ -1757,6 +3129,39 @@ 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->fh_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->fh_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->fh_type);
+ return -1;
+ }
+ return FNIC_TPORT_ADISC_RSP;
+ }
+
/*response from fabric */
rsp_type = fnic_fdls_expected_rsp(iport, ntohs(oxid));
@@ -1885,6 +3290,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->fh_s_id));
+ break;
case FNIC_FABRIC_LOGO_RSP:
fdls_process_fabric_logo_rsp(iport, fchdr);
break;
@@ -1894,7 +3314,8 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
if (fdls_is_oxid_in_fabric_range(oxid) &&
(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
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 db0a6978504e..d2c3ebce3209 100644
--- a/drivers/scsi/fnic/fnic_fdls.h
+++ b/drivers/scsi/fnic/fnic_fdls.h
@@ -385,7 +385,7 @@ 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);
+ struct fnic_tport_s *tport);
bool fdls_delete_tport(struct fnic_iport_s *iport,
struct fnic_tport_s *tport);
void fdls_fdmi_timer_callback(struct timer_list *t);
--
2.31.1
On 10/18/24 18:13, Karan Tilak Kumar wrote: > Add support for target based solicited requests and responses. > Add support for tport definitions and processing. > Add support for restarting the IT nexus. > > Reported-by: kernel test robot <lkp@intel.com> > Closes: > https://lore.kernel.org/oe-kbuild-all/202406120146.xchlZbqX-lkp@ > intel.com/ > > Reviewed-by: Sesidhar Baddela <sebaddel@cisco.com> > Co-developed-by: Gian Carlo Boffa <gcboffa@cisco.com> > Signed-off-by: Gian Carlo Boffa <gcboffa@cisco.com> > Co-developed-by: Arulprabhu Ponnusamy <arulponn@cisco.com> > Signed-off-by: Arulprabhu Ponnusamy <arulponn@cisco.com> > Co-developed-by: Arun Easi <aeasi@cisco.com> > Signed-off-by: Arun Easi <aeasi@cisco.com> > Co-developed-by: Karan Tilak Kumar <kartilak@cisco.com> > Signed-off-by: Karan Tilak Kumar <kartilak@cisco.com> > --- > Changes between v4 and v5: > Incorporate review comments from Martin: > Call fdls_get_tgt_oxid_pool. > Modify attribution appropriately. > > Changes between v2 and v3: > Fix issue found by kernel test robot. > Remove fnic_std_ba_acc definition to fix compilation > warning. > Incorporate review comments from Hannes: > Replace redundant definitions with standard definitions. > Replace static OXIDs with pool-based OXIDs for targets. > > Changes between v1 and v2: > Incorporate review comments from Hannes: > Use the correct kernel-doc format. > Replace htonll() with get_unaligned_be64(). > Replace fnic_del_fabric_timer_sync macro calls to function > calls. > Replace fnic_del_tport_timer_sync macro calls to function > calls. > Rename fc_abts_s to fc_tport_abts_s. > Modify fc_tport_abts_s to be a global frame. > Rename variable pfc_abts to tport_abts. > Replace definitions with standard definitions from > fc_els.h. > Modify functions with returns in the middle to if else > clauses. > Replace simultaneous use of fc_tport_abts_s and tport_abts with > just tport_abts. > --- > drivers/scsi/fnic/fdls_disc.c | 1569 +++++++++++++++++++++++++++++++-- > drivers/scsi/fnic/fnic.h | 6 + > drivers/scsi/fnic/fnic_fdls.h | 2 +- > 3 files changed, 1502 insertions(+), 75 deletions(-) > > diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c > index 3a995b76a864..52459f6bb589 100644 > --- a/drivers/scsi/fnic/fdls_disc.c > +++ b/drivers/scsi/fnic/fdls_disc.c > @@ -11,6 +11,10 @@ > #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 */ > /* > * Variables: > @@ -66,6 +70,20 @@ struct fc_std_rpn_id fnic_std_rpn_id_req = { > .ct_cmd = cpu_to_be16(FC_NS_RPN_ID)} > }; > > +/* > + * Variables: > + * did, sid, oxid > + */ > +struct fc_std_els_prli fnic_std_prli_req = { > + .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS, > + .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}, .fh_rx_id = 0xFFFF}, > + .els_prli = {.prli_cmd = ELS_PRLI, > + .prli_spp_len = 16, > + .prli_len = cpu_to_be16(0x14)}, > + .sp = {.spp_type = 0x08, .spp_flags = 0x0020, > + .spp_params = cpu_to_be32(0xA2)} > +}; > + > /* > * Variables: > * fh_s_id, port_id, port_name > @@ -143,9 +161,19 @@ struct fc_frame_header fc_std_fabric_abts = { > .fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */ > }; > > +struct fc_frame_header fc_std_tport_abts = { > + .fh_r_ctl = FC_RCTL_BA_ABTS, /* ABTS */ > + .fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS, > + .fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00, > + .fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000, .fh_rx_id = 0xFFFF, > + .fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */ > +}; > + > #define RETRIES_EXHAUSTED(iport) \ > (iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY) > > +#define FNIC_TPORT_MAX_NEXUS_RESTART (8) > + > /* > * For fabric requests and fdmi, once OXIDs are allocated from the pool > * (and a range) they are encoded with expected rsp type as > @@ -167,6 +195,14 @@ static void fdls_process_flogi_rsp(struct fnic_iport_s *iport, > void *rx_frame); > static void fnic_fdls_start_plogi(struct fnic_iport_s *iport); > static void fnic_fdls_start_flogi(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_tport_timer_callback(struct timer_list *t); > + > static void fdls_start_fabric_timer(struct fnic_iport_s *iport, > int timeout); > static void > @@ -182,6 +218,8 @@ void fdls_init_oxid_pool(struct fnic_iport_s *iport) > fdls_init_fabric_oxid_pool(&iport->fdmi_oxid_pool, > FDLS_FDMI_OXID_POOL_BASE, > FDLS_FDMI_OXID_POOL_SZ); > + > + fdls_init_tgt_oxid_pool(iport); > } > > uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, > @@ -313,6 +351,13 @@ static inline void fdls_schedule_fabric_oxid_free(struct fnic_iport_s > iport->fabric_oxid_pool.active_oxid_fabric_req); > } > > +static inline void fdls_schedule_tgt_oxid_free(struct fnic_iport_s *iport, > + struct fnic_tgt_oxid_pool_s > + *oxid_pool, uint16_t oxid) > +{ > + fdls_schedule_oxid_free(&oxid_pool->meta, oxid); > +} > + > int fnic_fdls_expected_rsp(struct fnic_iport_s *iport, uint16_t oxid) > { > struct fnic *fnic = iport->fnic; > @@ -340,6 +385,62 @@ static int fdls_is_oxid_in_fabric_range(uint16_t oxid) > (oxid_unmasked <= FDLS_FABRIC_OXID_POOL_END)); > } > > +void fdls_init_tgt_oxid_pool(struct fnic_iport_s *iport) > +{ > + memset(&iport->plogi_oxid_pool, 0, sizeof(iport->plogi_oxid_pool)); > + iport->plogi_oxid_pool.meta.oxid_base = FDLS_PLOGI_OXID_BASE; > + iport->plogi_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ; > + INIT_LIST_HEAD(&iport->plogi_oxid_pool.meta.reclaim_list); > + > + memset(&iport->prli_oxid_pool, 0, sizeof(iport->prli_oxid_pool)); > + iport->prli_oxid_pool.meta.oxid_base = FDLS_PRLI_OXID_BASE; > + iport->prli_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ; > + INIT_LIST_HEAD(&iport->prli_oxid_pool.meta.reclaim_list); > + > + memset(&iport->adisc_oxid_pool, 0, sizeof(iport->adisc_oxid_pool)); > + iport->adisc_oxid_pool.meta.oxid_base = FDLS_ADISC_OXID_BASE; > + iport->adisc_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ; > + INIT_LIST_HEAD(&iport->adisc_oxid_pool.meta.reclaim_list); > +} > + > +inline uint16_t fdls_alloc_tgt_oxid(struct fnic_iport_s *iport, > + struct fnic_tgt_oxid_pool_s *oxid_pool) > +{ > + uint16_t oxid; > + > + oxid = fdls_alloc_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap); > + return oxid; > +} Do a 'return fdls_alloc_oxid()' and drop the intermediate variable. > + > +inline void fdls_free_tgt_oxid(struct fnic_iport_s *iport, > + struct fnic_tgt_oxid_pool_s *oxid_pool, > + uint16_t oxid) > +{ > + fdls_free_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap, oxid); > +} > + > +static struct fnic_tgt_oxid_pool_s *fdls_get_tgt_oxid_pool(struct fnic_tport_s > + *tport) > +{ > + struct fnic_iport_s *iport = (struct fnic_iport_s *)tport->iport; > + struct fnic_tgt_oxid_pool_s *oxid_pool = NULL; > + > + switch (tport->state) { > + case FDLS_TGT_STATE_PLOGI: > + oxid_pool = &iport->plogi_oxid_pool; > + break; > + case FDLS_TGT_STATE_PRLI: > + oxid_pool = &iport->prli_oxid_pool; > + break; > + case FDLS_TGT_STATE_ADISC: > + oxid_pool = &iport->adisc_oxid_pool; > + break; > + default: > + break; > + } > + return oxid_pool; > +} > + > inline void fnic_del_fabric_timer_sync(struct fnic *fnic) > { > fnic->iport.fabric.del_timer_inprogress = 1; > @@ -383,6 +484,56 @@ 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(fnic, tport); > + 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_frame_header tport_abort = fc_std_tport_abts; > + struct fc_frame_header *tport_abts = &tport_abort; > + > + hton24(s_id, iport->fcid); > + hton24(d_id, tport->fcid); > + FNIC_STD_SET_S_ID(tport_abts, s_id); > + FNIC_STD_SET_D_ID(tport_abts, d_id); > + tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED; > + > + tport_abts->fh_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, tport_abts, sizeof(struct fc_frame_header)); > + /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */ > + fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov); > +} > + > static void fdls_send_fabric_abts(struct fnic_iport_s *iport) > { > uint8_t fcid[3]; > @@ -614,6 +765,176 @@ 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_std_els_adisc 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_std_els_adisc)); > + FNIC_STD_SET_R_CTL(&adisc.fchdr, 0x22); > + FNIC_STD_SET_TYPE(&adisc.fchdr, 0x01); > + FNIC_STD_SET_F_CTL(&adisc.fchdr, FNIC_ELS_REQ_FCTL << 16); > + FNIC_STD_SET_RX_ID(&adisc.fchdr, cpu_to_be16(0xFFFF)); > + > + hton24(s_id, iport->fcid); > + hton24(d_id, tport->fcid); > + FNIC_STD_SET_S_ID(&adisc.fchdr, s_id); > + FNIC_STD_SET_D_ID(&adisc.fchdr, d_id); > + > + oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->adisc_oxid_pool)); > + 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_STD_SET_OX_ID((&adisc.fchdr), oxid); > + FNIC_STD_SET_NPORT_NAME(&adisc.els.adisc_wwpn, > + le64_to_cpu(iport->wwpn)); > + FNIC_STD_SET_NODE_NAME(&adisc.els.adisc_wwnn, le64_to_cpu(iport->wwnn)); > + > + memcpy(adisc.els.adisc_port_id, s_id, 3); That feels wrong. Isn't port_id a three-byte value in big endian? > + adisc.els.adisc_cmd = ELS_ADISC; > + > + 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_std_els_adisc)); > + /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */ > + fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov); > +} > + > +bool 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)) { > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "tport fcid 0x%x: tport state is offlining/offline\n", > + tport->fcid); > + return false; > + } > + > + 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(fnic, tport); > + 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 false; > + } > + 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); > + } > + } > + return true; > +} > + > +static void > +fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport) > +{ > + struct fc_std_flogi 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_std_plogi_req, sizeof(struct fc_std_flogi)); > + > + hton24(s_id, iport->fcid); > + hton24(d_id, tport->fcid); > + > + FNIC_STD_SET_S_ID(&plogi.fchdr, s_id); > + FNIC_STD_SET_D_ID(&plogi.fchdr, d_id); > + FNIC_LOGI_SET_RDF_SIZE(&plogi.els, iport->max_payload_size); > + > + oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool)); > + 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_STD_SET_OX_ID((&plogi.fchdr), oxid); > + FNIC_LOGI_SET_NPORT_NAME(&plogi.els, iport->wwpn); > + FNIC_LOGI_SET_NODE_NAME(&plogi.els, iport->wwnn); > + > + timeout = max(2 * iport->e_d_tov, iport->plogi_timeout); > + > + > + fnic_send_fcoe_frame(iport, &plogi, sizeof(struct fc_std_flogi)); > + /* 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_std_flogi *plogi_rsp) > +{ > + uint16_t b2b_rdf_size = > + be16_to_cpu(FNIC_LOGI_RDF_SIZE(&plogi_rsp->els)); > + uint16_t spc3_rdf_size = > + be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & 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_std_rft_id rft_id; > @@ -690,6 +1011,48 @@ 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_std_els_prli 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, &iport->prli_oxid_pool)); > + 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_std_prli_req, sizeof(struct fc_std_els_prli)); Memcpy again. > + > + hton24(s_id, iport->fcid); > + hton24(d_id, tport->fcid); > + > + FNIC_STD_SET_S_ID((&prli.fchdr), s_id); > + FNIC_STD_SET_D_ID((&prli.fchdr), d_id); > + FNIC_STD_SET_OX_ID((&prli.fchdr), oxid); > + > + timeout = max(2 * iport->e_d_tov, iport->plogi_timeout); > + > + fdls_get_tgt_oxid_pool(tport); > + fnic_send_fcoe_frame(iport, &prli, sizeof(struct fc_std_els_prli)); > + /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */ > + fdls_start_tport_timer(iport, tport, timeout); > +} > + > /** > * fdls_send_fabric_logo - Send flogo to the fcf > * @iport: Handle to fnic iport > @@ -742,6 +1105,212 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport) > fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo)); > } > > +/** > + * fdls_tgt_logout - Send plogo to the remote port > + * @iport: Handle to fnic iport > + * @tport: Handle to remote port > + * > + * This function does not change or check the fabric/tport state. > + * It the caller's responsibility to set the appropriate tport/fabric > + * state when this is called. Normally that is fdls_tgt_state_plogo. > + * 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_std_logo logo; > + uint8_t s_id[3]; > + uint8_t d_id[3]; > + struct fnic *fnic = iport->fnic; > + uint16_t oxid; > + > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "Sending logo to tport fcid: 0x%x", tport->fcid); > + memcpy(&logo, &fnic_std_logo_req, sizeof(struct fc_std_logo)); And here. > + > + hton24(s_id, iport->fcid); > + hton24(d_id, tport->fcid); > + > + FNIC_STD_SET_S_ID((&logo.fchdr), s_id); > + FNIC_STD_SET_D_ID((&logo.fchdr), d_id); > + > + oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool)); > + FNIC_STD_SET_OX_ID((&logo.fchdr), oxid); > + > + memcpy(&logo.els.fl_n_port_id, s_id, 3); > + FNIC_STD_SET_NPORT_NAME(&logo.els.fl_n_port_wwn, > + le64_to_cpu(iport->wwpn)); > + > + > + fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo)); > +} > + > +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; > + bool retval = true; > + > + 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; > + > + retval = fdls_delete_tport(iport, tport); > + if (retval != true) { > + FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num, > + "Error deleting tport: 0x%x", fcid); > + return; > + } > + > + 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; Odd. Can you have two entries with the same fcid in the list? > + } > + 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; Same argument here. I would have expected wwpns to be unique ... > + } > + return NULL; > +} > + > void fdls_fabric_timer_callback(struct timer_list *t) > { > struct fnic_fdls_fabric_s *fabric = from_timer(fabric, t, retry_timer); > @@ -847,90 +1416,588 @@ void fdls_fabric_timer_callback(struct timer_list *t) > /* ABTS has timed out */ > fdls_schedule_fabric_oxid_free(iport); > 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); > + } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) { > + /* RFT_ID timed out send abts */ > + fdls_send_fabric_abts(iport); > + } else { > + /* ABTS has timed out */ > + fdls_schedule_fabric_oxid_free(iport); > + 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); > + } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) > + /* SCR has timed out. Send abts */ > + fdls_send_fabric_abts(iport); > + else { > + /* ABTS has timed out */ > + fdls_schedule_fabric_oxid_free(iport); > + 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); > + } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) { > + /* gpn_ft has timed out. Send abts */ > + fdls_send_fabric_abts(iport); > + } else { > + /* ABTS has timed out */ > + fdls_schedule_fabric_oxid_free(iport); > + 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: > + fnic_fdls_start_flogi(iport); /* Placeholder call */ > + 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)) { > + tport->flags &= ~FNIC_FDLS_RETRY_FRAME; > + fdls_send_tgt_plogi(iport, tport); > + } else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) { > + /* Plogi frame has timed out, send abts */ > + fdls_send_tport_abts(iport, tport); > + } else if (tport->retry_counter < iport->max_plogi_retries) { > + /* > + * ABTS has timed out > + */ > + fdls_schedule_tgt_oxid_free(iport, > + &iport->plogi_oxid_pool, > + oxid); > + fdls_send_tgt_plogi(iport, tport); > + } else { > + /* exceeded plogi retry count */ > + fdls_schedule_tgt_oxid_free(iport, > + &iport->plogi_oxid_pool, > + 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)) { > + tport->flags &= ~FNIC_FDLS_RETRY_FRAME; > + fdls_send_tgt_prli(iport, tport); > + } else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) { > + /* PRLI has time out, send abts */ > + fdls_send_tport_abts(iport, tport); > + } else { > + /* ABTS has timed out for prli, we go back to PLOGI */ > + fdls_schedule_tgt_oxid_free(iport, > + &iport->prli_oxid_pool, > + 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 an ABTS */ > + if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) { > + fdls_send_tport_abts(iport, tport); > + } else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED) > + && (tport->retry_counter < FDLS_RETRY_COUNT)) { > + /* > + * ABTS has timed out > + */ > + fdls_schedule_tgt_oxid_free(iport, > + &iport->adisc_oxid_pool, > + oxid); > + fdls_send_tgt_adisc(iport, tport); > + } else { > + /* exceeded retry count */ > + fdls_schedule_tgt_oxid_free(iport, > + &iport->adisc_oxid_pool, > + 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); > + } > + break; > + 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_frame_header *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_std_els_adisc *adisc_rsp = (struct fc_std_els_adisc *)fchdr; > + struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr; > + struct fnic *fnic = iport->fnic; > + > + fcid = FNIC_STD_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->fh_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; > + } > + > + oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr)); > + fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, oxid); > + > + switch (adisc_rsp->els.adisc_cmd) { > + case ELS_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(fnic, tport); > + } > + tport->timer_pending = 0; > + tport->retry_counter = 0; > + frame_wwnn = get_unaligned_be64(&adisc_rsp->els.adisc_wwnn); > + frame_wwpn = get_unaligned_be64(&adisc_rsp->els.adisc_wwpn); > + 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 ELS_LS_RJT: > + if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY) > + || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB)) > + && (tport->retry_counter < FDLS_RETRY_COUNT)) { > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "ADISC ret ELS_LS_RJT 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 ELS_LS_RJT from target: 0x%x", > + tgt_fcid); > + fdls_delete_tport(iport, tport); > + } > + break; > + } > +} > + > + > +static void > +fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport, > + struct fc_frame_header *fchdr) > +{ > + uint32_t tgt_fcid; > + struct fnic_tport_s *tport; > + uint8_t *fcid; > + uint16_t oxid; > + struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *)fchdr; > + struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr; > + int max_payload_size; > + struct fnic *fnic = iport->fnic; > + > + fcid = FNIC_STD_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. Drop the frame and restart nexus"); > + fdls_target_restart_nexus(tport); > + return; > + } > + > + if (fchdr->fh_ox_id != 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; > + } > + > + oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr)); > + fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, oxid); > + > + switch (plogi_rsp->els.fl_cmd) { > + case ELS_LS_ACC: > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "PLOGI accepted by target: 0x%x", tgt_fcid); > + break; > + > + case ELS_LS_RJT: > + if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY) > + || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB)) > + && (tport->retry_counter < iport->max_plogi_retries)) { > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "PLOGI ret ELS_LS_RJT 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 ELS_LS_RJT from target: 0x%x", > + tgt_fcid); > + 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(fnic, tport); > + } > + > + tport->timer_pending = 0; > + tport->wwpn = get_unaligned_be64(&FNIC_LOGI_PORT_NAME(&plogi_rsp->els)); > + tport->wwnn = get_unaligned_be64(&FNIC_LOGI_NODE_NAME(&plogi_rsp->els)); > + > + /* 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_frame_header *fchdr) > +{ > + uint32_t tgt_fcid; > + struct fnic_tport_s *tport; > + uint8_t *fcid; > + uint16_t oxid; > + struct fc_std_els_prli *prli_rsp = (struct fc_std_els_prli *)fchdr; > + struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr; > + struct fnic_tport_event_s *tport_add_evt; > + struct fnic *fnic = iport->fnic; > + bool mismatched_tgt = false; > + > + fcid = FNIC_STD_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. Drop frame. Restarting nexus"); > + fdls_target_restart_nexus(tport); > + return; > + } > + > + if (fchdr->fh_ox_id != 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; > + } > + > + oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr)); > + fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid); > + > + switch (prli_rsp->els_prli.prli_cmd) { > + case ELS_LS_ACC: > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "PRLI accepted from target: 0x%x", tgt_fcid); > + > + if (prli_rsp->sp.spp_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); > - } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) { > - /* RFT_ID timed out send abts */ > - fdls_send_fabric_abts(iport); > - } else { > - /* ABTS has timed out */ > - fdls_schedule_fabric_oxid_free(iport); > - 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 */ > + if (mismatched_tgt) { > + fdls_tgt_logout(iport, tport); > + fdls_delete_tport(iport, tport); > + return; > } > 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); > - } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) > - /* SCR has timed out. Send abts */ > - fdls_send_fabric_abts(iport); > - else { > - /* ABTS has timed out */ > - fdls_schedule_fabric_oxid_free(iport); > + case ELS_LS_RJT: > + if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY) > + || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB)) > + && (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); > - } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) { > - /* gpn_ft has timed out. Send abts */ > - fdls_send_fabric_abts(iport); > + "PRLI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x", > + tgt_fcid); > + > + /*Retry Plogi again from the timer routine. */ > + tport->flags |= FNIC_FDLS_RETRY_FRAME; > + return; > } else { > - /* ABTS has timed out */ > - fdls_schedule_fabric_oxid_free(iport); > - 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 ELS_LS_RJT from target: 0x%x", > + tgt_fcid); > + > + fdls_tgt_logout(iport, tport); > + fdls_delete_tport(iport, tport); > + return; > } > break; > + > default: > - fnic_fdls_start_flogi(iport); /* Placeholder call */ > + 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(fnic, tport); > + } > + tport->timer_pending = 0; > + > + /* Learn Service Params */ > + tport->fcp_csp = be32_to_cpu(prli_rsp->sp.spp_params); > + tport->retry_counter = 0; > + > + if (prli_rsp->sp.spp_params & 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_frame_header *fchdr) > @@ -1179,7 +2246,8 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport, > iport); > if (iport->fabric.timer_pending) { > FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > - "Canceling fabric disc timer %p\n", iport); > + "Canceling fabric disc timer %p\n", > + iport); > fnic_del_fabric_timer_sync(fnic); > } > fdls->timer_pending = 0; > @@ -1192,6 +2260,104 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport, > } > } > > +static void > +fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport, > + struct fc_frame_header *fchdr, int len) > +{ > + struct fc_gpn_ft_rsp_iu *gpn_ft_tgt; > + 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 *)((uint8_t *) fchdr + > + sizeof(struct fc_frame_header) > + + sizeof(struct fc_ct_hdr)); > + len -= sizeof(struct fc_frame_header) + sizeof(struct fc_ct_hdr); > + > + 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); > + 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); > + } > + 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_frame_header *fchdr, int len) > @@ -1200,6 +2366,9 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, > struct fc_std_gpn_ft *gpn_ft_rsp = (struct fc_std_gpn_ft *) 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, > @@ -1239,12 +2408,74 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, > case FC_FS_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(fnic); > + } > + 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_FS_RJT: > reason_code = gpn_ft_rsp->fc_std_ct_hdr.ct_reason; > FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > "0x%x: GPNFT_RSP Reject reason: %d", iport->fcid, reason_code); > + > + if (((reason_code == FC_FS_RJT_BSY) > + || (reason_code == FC_FS_RJT_UNABL)) > + && (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(fnic); > + } > + 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: > @@ -1494,8 +2725,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, > struct fc_frame_header *fchdr) > { > uint32_t s_id; > - struct fc_std_abts_ba_acc *ba_acc = > - (struct fc_std_abts_ba_acc *) fchdr; > + struct fc_std_abts_ba_acc *ba_acc = (struct fc_std_abts_ba_acc *)fchdr; > struct fc_std_abts_ba_rjt *ba_rjt; > uint32_t fabric_state = iport->fabric.state; > struct fnic *fnic = iport->fnic; > @@ -1660,6 +2890,148 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, > } > } > > +static void > +fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport, > + struct fc_frame_header *fchdr) > +{ > + uint32_t s_id; > + struct fnic_tport_s *tport; > + uint32_t tport_state; > + struct fc_std_abts_ba_acc *ba_acc; > + struct fc_std_abts_ba_rjt *ba_rjt; > + uint16_t oxid; > + struct fnic *fnic = iport->fnic; > + > + s_id = ntoh24(fchdr->fh_s_id); > + ba_acc = (struct fc_std_abts_ba_acc *)fchdr; > + ba_rjt = (struct fc_std_abts_ba_rjt *)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(fnic, tport); > + } > + 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->fh_ox_id); > + > + /*This abort rsp is for ADISC */ > + if ((oxid >= FDLS_ADISC_OXID_BASE) && (oxid < FDLS_TGT_OXID_POOL_END)) { > + if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) { > + FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num, > + "OX_ID: 0x%x tgt_fcid: 0x%x rcvd tgt adisc abts resp BA_ACC", > + be16_to_cpu(ba_acc->acc.ba_ox_id), > + tport->fcid); > + } else if (fchdr->fh_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->rjt.br_reason, > + ba_rjt->rjt.br_explan); > + } > + if ((tport->retry_counter < FDLS_RETRY_COUNT) > + && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) { > + fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, > + oxid); > + fdls_send_tgt_adisc(iport, tport); > + return; > + } > + > + fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, 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->fh_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->fh_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->fh_ox_id); > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "reason code: 0x%x reason code explanation: 0x%x", > + ba_rjt->rjt.br_reason, > + ba_rjt->rjt.br_explan); > + } > + if ((tport->retry_counter < iport->max_plogi_retries) > + && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) { > + fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, > + oxid); > + fdls_send_tgt_plogi(iport, tport); > + return; > + } > + > + fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, 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->fh_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->fh_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->fh_ox_id); > + FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num, > + "reason code: 0x%x reason code explanation: 0x%x", > + ba_rjt->rjt.br_reason, > + ba_rjt->rjt.br_explan); > + } > + if ((tport->retry_counter < FDLS_RETRY_COUNT) > + && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) { > + fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid); > + fdls_send_tgt_prli(iport, tport); > + return; > + } > + fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, 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 > */ > @@ -1757,6 +3129,39 @@ 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->fh_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->fh_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->fh_type); > + return -1; > + } > + return FNIC_TPORT_ADISC_RSP; > + } > + > /*response from fabric */ > rsp_type = fnic_fdls_expected_rsp(iport, ntohs(oxid)); > > @@ -1885,6 +3290,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->fh_s_id)); > + break; > case FNIC_FABRIC_LOGO_RSP: > fdls_process_fabric_logo_rsp(iport, fchdr); > break; > @@ -1894,7 +3314,8 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame, > if (fdls_is_oxid_in_fabric_range(oxid) && > (iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) { > 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 db0a6978504e..d2c3ebce3209 100644 > --- a/drivers/scsi/fnic/fnic_fdls.h > +++ b/drivers/scsi/fnic/fnic_fdls.h > @@ -385,7 +385,7 @@ 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); > + struct fnic_tport_s *tport); > bool fdls_delete_tport(struct fnic_iport_s *iport, > struct fnic_tport_s *tport); > void fdls_fdmi_timer_callback(struct timer_list *t); Cheers, Hannes -- Dr. Hannes Reinecke Kernel Storage Architect hare@suse.de +49 911 74053 688 SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
© 2016 - 2024 Red Hat, Inc.