diff mbox series

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

Message ID 20240809164240.47561-5-kartilak@cisco.com
State New
Headers show
Series Introduce support for Fabric Discovery and | expand

Commit Message

Karan Tilak Kumar Aug. 9, 2024, 4:42 p.m. UTC
Add support for target based solicited requests and responses.
Add support for tport definitions and processing.
Add support for restarting the IT nexus.

Reviewed-by: Sesidhar Baddela <sebaddel@cisco.com>
Reviewed-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Reviewed-by: Gian Carlo Boffa <gcboffa@cisco.com>
Signed-off-by: Karan Tilak Kumar <kartilak@cisco.com>
---
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 | 1580 ++++++++++++++++++++++++++++++---
 drivers/scsi/fnic/fnic.h      |    6 +
 drivers/scsi/fnic/fnic_fdls.h |    4 +
 3 files changed, 1473 insertions(+), 117 deletions(-)
diff mbox series

Patch

diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index 2dda1ce9a55f..696f8d926865 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -11,6 +11,8 @@ 
 #include <scsi/fc/fc_fcp.h>
 #include <linux/utsname.h>
 
+#define FC_FC4_TYPE_SCSI 0x08
+
 static void fdls_send_rpn_id(struct fnic_iport_s *iport);
 
 /* Frame initialization */
@@ -59,6 +61,19 @@  struct fc_rpn_id_s fnic_rpn_id_req = {
 				  .command = FC_CT_RPN_CMD}
 };
 
+/*
+ * Variables:
+ * did, sid, oxid
+ */
+struct fc_els_prli_s fnic_prli_req = {
+	.fchdr = {.r_ctl = 0x22, .type = 0x01,
+			  .f_ctl = FNIC_ELS_REQ_FCTL, .rx_id = 0xFFFF},
+	.command = ELS_PRLI,
+	.page_len = 16,
+	.payload_len = 0x1400,
+	.sp = {.type = 0x08, .flags = 0x0020, .csp = 0xA2000000}
+};
+
 /*
  * Variables:
  * fh_s_id, port_id, port_name
@@ -128,14 +143,61 @@  struct fc_hdr_s fc_fabric_abts_s = {
 	.param = 0x00000000,	/* bit:0 = 0 Abort a exchange */
 };
 
+struct fc_hdr_s fc_tport_abts_s = {
+	.r_ctl = 0x81,			/* ABTS */
+	.cs_ctl = 0x00, .type = 0x00, .f_ctl = FNIC_REQ_ABTS_FCTL, .seq_id =
+			0x00, .df_ctl = 0x00, .seq_cnt = 0x0000, .rx_id = 0xFFFF,
+	.param = 0x00000000,	/* bit:0 = 0 Abort a exchange */
+};
+
 #define RETRIES_EXHAUSTED(iport)      \
 	(iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)
 
+#define FNIC_TPORT_MAX_NEXUS_RESTART (8)
+
+/* Private Functions */
 static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 		   struct fc_hdr_s *fchdr, void *rx_frame);
 static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+									  uint32_t fcid,
+									  uint64_t wwpn);
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport);
+static void fdls_start_tport_timer(struct fnic_iport_s *iport,
+					struct fnic_tport_s *tport, int timeout);
+
 static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
 			int timeout);
+static void fdls_tport_timer_callback(struct timer_list *t);
+
+static uint16_t fdls_alloc_tgt_oxid(struct fnic_iport_s *iport,
+									uint16_t base)
+{
+	int i;
+	int start, end;
+
+	start = base - FDLS_PLOGI_OXID_BASE;
+	end = start + FDLS_TGT_OXID_BLOCK_SZ;
+
+	for (i = start; i < end; i++) {
+		if (iport->tgt_oxid_pool[i] == 0) {
+			iport->tgt_oxid_pool[i] = 1;
+			return (i + FDLS_PLOGI_OXID_BASE);
+		}
+	}
+	return 0xFFFF;
+}
+
+static void fdls_free_tgt_oxid(struct fnic_iport_s *iport, uint16_t oxid)
+{
+	struct fnic *fnic = iport->fnic;
+
+	if (iport->tgt_oxid_pool[oxid - FDLS_PLOGI_OXID_BASE] != 1) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Freeing unused OXID: 0x%x", oxid);
+	}
+	iport->tgt_oxid_pool[oxid - FDLS_PLOGI_OXID_BASE] = 0;
+}
 
 inline void fnic_del_fabric_timer_sync(struct fnic *fnic)
 {
@@ -180,6 +242,55 @@  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_hdr_s *tport_abts = &fc_tport_abts_s;
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+	FNIC_SET_S_ID(tport_abts, s_id);
+	FNIC_SET_D_ID(tport_abts, d_id);
+	tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
+
+	tport_abts->ox_id = tport->oxid_used;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS sending tport abts: tport->state: %d ",
+				 tport->state);
+
+	fnic_send_fcoe_frame(iport, &fc_tport_abts_s, sizeof(struct fc_hdr_s));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, 2 * iport->r_a_tov);
+}
+
 static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
 {
 	uint8_t fcid[3];
@@ -357,6 +468,169 @@  static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
 	fdls_set_state((&iport->fabric), fdls_state);
 }
 
+static void
+fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fc_els_adisc_s adisc;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+
+	memset(&adisc, 0, sizeof(struct fc_els_adisc_s));
+	adisc.fchdr.r_ctl = 0x22;
+	adisc.fchdr.type = 0x01;
+	adisc.fchdr.f_ctl = FNIC_ELS_REQ_FCTL;
+	adisc.fchdr.rx_id = 0xFFFF;
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+	FNIC_SET_S_ID((&adisc.fchdr), s_id);
+	FNIC_SET_D_ID((&adisc.fchdr), d_id);
+
+	oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_ADISC_OXID_BASE));
+	if (oxid == 0xFFFF) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Failed to allocate OXID to send ADISC %p", iport);
+		return;
+	}
+
+	tport->oxid_used = oxid;
+	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+	FNIC_SET_OX_ID((&adisc.fchdr), oxid);
+	FNIC_SET_NPORT_NAME(adisc, iport->wwpn);
+	FNIC_SET_NODE_NAME(adisc, iport->wwnn);
+
+	memcpy(adisc.fcid, s_id, 3);
+	adisc.command = 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_els_adisc_s));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+
+void fdls_delete_tport(struct fnic_iport_s *iport,
+					   struct fnic_tport_s *tport)
+{
+	struct fnic_tport_event_s *tport_del_evt;
+	struct fnic *fnic = iport->fnic;
+
+	if ((tport->state == FDLS_TGT_STATE_OFFLINING)
+		|| (tport->state == FDLS_TGT_STATE_OFFLINE))
+		return;
+
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
+	/*
+	 * By setting this flag, the tport will not be seen in a look-up
+	 * in an RSCN. Even if we move to multithreaded model, this tport
+	 * will be destroyed and a new RSCN will have to create a new one
+	 */
+	tport->flags |= FNIC_FDLS_TPORT_TERMINATING;
+
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport fcid 0x%x: Canceling disc timer\n",
+					 tport->fcid);
+		fnic_del_tport_timer_sync(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;
+			}
+			tport_del_evt->event = TGT_EV_RPORT_DEL;
+			tport_del_evt->arg1 = (void *) tport;
+			list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+			queue_work(fnic_event_queue, &fnic->tport_work);
+		} else {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport 0x%x not reg with scsi_transport. Freeing locally",
+				 tport->fcid);
+			list_del(&tport->links);
+			kfree(tport);
+		}
+	}
+}
+
+static void
+fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fc_els_s plogi;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+	uint32_t timeout;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Send tgt PLOGI to fcid: 0x%x", tport->fcid);
+
+	memcpy(&plogi, &fnic_plogi_req, sizeof(struct fc_els_s));
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+
+	FNIC_SET_S_ID((&plogi.fchdr), s_id);
+	FNIC_SET_D_ID((&plogi.fchdr), d_id);
+	FNIC_SET_RDF_SIZE(plogi.u.csp_plogi, iport->max_payload_size);
+
+	oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_PLOGI_OXID_BASE));
+	if (oxid == 0xFFFF) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
+				 iport->fcid, tport->fcid);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "send tgt PLOGI: tgt fcid: 0x%x oxid: 0x%x", tport->fcid,
+				 ntohs(oxid));
+	tport->oxid_used = oxid;
+	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+	FNIC_SET_OX_ID((&plogi.fchdr), oxid);
+	FNIC_SET_NPORT_NAME(plogi, iport->wwpn);
+	FNIC_SET_NODE_NAME(plogi, iport->wwnn);
+
+	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+	fnic_send_fcoe_frame(iport, &plogi, sizeof(struct fc_els_s));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, timeout);
+}
+
+static uint16_t
+fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
+					  struct fc_els_s *plogi_rsp)
+{
+	uint16_t b2b_rdf_size = ntohs(plogi_rsp->u.csp_plogi.b2b_rdf_size);
+	uint16_t spc3_rdf_size =
+		((uint16_t) (plogi_rsp->spc3[6] << 8 | plogi_rsp->spc3[7]) &
+		 FNIC_FC_C3_RDF);
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
+			 b2b_rdf_size, spc3_rdf_size);
+
+	return MIN(b2b_rdf_size, spc3_rdf_size);
+}
+
 static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
 {
 	struct fc_rft_id rft_id;
@@ -406,6 +680,47 @@  static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
 	fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
 }
 
+static void
+fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fc_els_prli_s prli;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+	uint32_t timeout;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS sending PRLI to tgt: 0x%x", tport->fcid);
+
+	oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_PRLI_OXID_BASE));
+	if (oxid == 0xFFFF) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Failed to allocate OXID to send PRLI %p", iport);
+		return;
+	}
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS sending PRLI to tgt: 0x%x OXID: 0x%x", tport->fcid,
+				 ntohs(oxid));
+
+	tport->oxid_used = oxid;
+	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+	memcpy(&prli, &fnic_prli_req, sizeof(struct fc_els_prli_s));
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+
+	FNIC_SET_S_ID((&prli.fchdr), s_id);
+	FNIC_SET_D_ID((&prli.fchdr), d_id);
+	FNIC_SET_OX_ID((&prli.fchdr), oxid);
+
+	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+	fnic_send_fcoe_frame(iport, &prli, sizeof(struct fc_els_prli_s));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, timeout);
+}
+
 /**
  * fdls_send_fabric_logo - Send flogo to the fcf
  * @iport: Handle to fnic iport
@@ -442,6 +757,201 @@  void fdls_send_fabric_logo(struct fnic_iport_s *iport)
 	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_logo_req_s));
 }
 
+/**
+ * fdls_tgt_logout - 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_logo_req_s logo;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Sending logo to tport fcid: 0x%x", tport->fcid);
+	memcpy(&logo, &fnic_logo_req, sizeof(struct fc_logo_req_s));
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+
+	FNIC_SET_S_ID((&logo.fchdr), s_id);
+	FNIC_SET_D_ID((&logo.fchdr), d_id);
+	FNIC_SET_OX_ID((&logo.fchdr), FNIC_TLOGO_REQ_OXID);
+
+	memcpy(&logo.fcid, s_id, 3);
+	logo.wwpn = get_unaligned_be64(&iport->wwpn);
+
+	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_logo_req_s));
+}
+
+static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
+{
+	struct fnic_tport_s *tport, *next;
+	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: Starting FDLS target discovery", iport->fcid);
+
+	list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+		if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+			|| (iport->state != FNIC_IPORT_STATE_READY)) {
+			break;
+		}
+		/* if we marked the tport as deleted due to GPN_FT
+		 * We should not send ADISC anymore
+		 */
+		if ((tport->state == FDLS_TGT_STATE_OFFLINING) ||
+			(tport->state == FDLS_TGT_STATE_OFFLINE))
+			continue;
+
+		/* For tports which have received RSCN */
+		if (tport->flags & FNIC_FDLS_TPORT_SEND_ADISC) {
+			tport->retry_counter = 0;
+			fdls_set_tport_state(tport, FDLS_TGT_STATE_ADISC);
+			tport->flags &= ~FNIC_FDLS_TPORT_SEND_ADISC;
+			fdls_send_tgt_adisc(iport, tport);
+			continue;
+		}
+		if (fdls_get_tport_state(tport) != FDLS_TGT_STATE_INIT) {
+			/* Not a new port, skip  */
+			continue;
+		}
+		tport->retry_counter = 0;
+		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+		fdls_send_tgt_plogi(iport, tport);
+	}
+	fdls_set_state((&iport->fabric), FDLS_STATE_TGT_DISCOVERY);
+}
+
+/*
+ * Function to restart the IT nexus if we received any out of
+ * sequence PLOGI/PRLI  response from the target.
+ * The memory for the new tport structure is allocated
+ * inside fdls_create_tport and added to the iport's tport list.
+ * This will get freed later during tport_offline/linkdown
+ * or module unload. The new_tport pointer will go out of scope
+ * safely since the memory it is
+ * pointing to it will be freed later
+ */
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
+{
+	struct fnic_iport_s *iport = tport->iport;
+	struct fnic_tport_s *new_tport = NULL;
+	uint32_t fcid;
+	uint64_t wwpn;
+	int nexus_restart_count;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport fcid: 0x%x state: %d restart_count: %d",
+				 tport->fcid, tport->state, tport->nexus_restart_count);
+
+	fcid = tport->fcid;
+	wwpn = tport->wwpn;
+	nexus_restart_count = tport->nexus_restart_count;
+
+	fdls_delete_tport(iport, tport);
+
+	if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Exceeded nexus restart retries tport: 0x%x", fcid);
+		return;
+	}
+
+	/*
+	 * Allocate memory for the new tport and add it to
+	 * iport's tport list.
+	 * This memory will be freed during tport_offline/linkdown
+	 * or module unload. The pointer new_tport is safe to go
+	 * out of scope when this function returns, since the memory
+	 * it is pointing to is guaranteed to be freed later
+	 * as mentioned above.
+	 */
+	new_tport = fdls_create_tport(iport, fcid, wwpn);
+	if (!new_tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Error creating new tport: 0x%x", fcid);
+		return;
+	}
+
+	new_tport->nexus_restart_count = nexus_restart_count + 1;
+	fdls_send_tgt_plogi(iport, new_tport);
+	fdls_set_tport_state(new_tport, FDLS_TGT_STATE_PLOGI);
+}
+
+struct fnic_tport_s *fnic_find_tport_by_fcid(struct fnic_iport_s *iport,
+									 uint32_t fcid)
+{
+	struct fnic_tport_s *tport, *next;
+
+	list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+		if ((tport->fcid == fcid)
+			&& !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+			return tport;
+	}
+	return NULL;
+}
+
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+								  uint32_t fcid, uint64_t wwpn)
+{
+	struct fnic_tport_s *tport;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);
+
+	tport = kzalloc(sizeof(struct fnic_tport_s), GFP_ATOMIC);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Memory allocation failure while creating tport: 0x%x\n",
+			 fcid);
+		return NULL;
+	}
+
+	tport->max_payload_size = FNIC_FCOE_MAX_FRAME_SZ;
+	tport->r_a_tov = FNIC_R_A_TOV_DEF;
+	tport->e_d_tov = FNIC_E_D_TOV_DEF;
+	tport->fcid = fcid;
+	tport->wwpn = wwpn;
+	tport->iport = iport;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Need to setup tport timer callback");
+
+	timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Added tport 0x%x", tport->fcid);
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
+	list_add_tail(&tport->links, &iport->tport_list);
+	atomic_set(&tport->in_flight, 0);
+	return tport;
+}
+
+struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
+									 uint64_t wwpn)
+{
+	struct fnic_tport_s *tport, *next;
+
+	list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+		if ((tport->wwpn == wwpn)
+			&& !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+			return tport;
+	}
+	return NULL;
+}
+
 void fdls_fabric_timer_callback(struct timer_list *t)
 {
 	struct fnic_fdls_fabric_s *fabric = from_timer(fabric, t, retry_timer);
@@ -481,11 +991,8 @@  void fdls_fabric_timer_callback(struct timer_list *t)
 			&& (iport->fabric.retry_counter < iport->max_flogi_retries)) {
 			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
 			fdls_send_fabric_flogi(iport);
-			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			return;
-		}
-		/* Flogi has time out 2*ed_tov send abts */
-		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+			/* Flogi has time out 2*ed_tov send abts */
 			fdls_send_fabric_abts(iport);
 		} else {
 			/* Flogi ABTS has timed out and we have waited
@@ -506,11 +1013,8 @@  void fdls_fabric_timer_callback(struct timer_list *t)
 			&& (iport->fabric.retry_counter < iport->max_plogi_retries)) {
 			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
 			fdls_send_fabric_plogi(iport);
-			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			return;
-		}
+		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
 		/* Plogi has timed out 2*ed_tov send abts */
-		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
 			fdls_send_fabric_abts(iport);
 		} else {
 			/* plogi ABTS has timed out and we have waited
@@ -524,130 +1028,620 @@  void fdls_fabric_timer_callback(struct timer_list *t)
 				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
 							 "Exceeded max PLOGI retries");
 		}
-		break;
-	case FDLS_STATE_RPN_ID:
-		/* Rpn_id 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_rpn_id(iport);
-			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		break;
+	case FDLS_STATE_RPN_ID:
+		/* Rpn_id 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_rpn_id(iport);
+		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+			/* RPN has timed out. Send abts */
+			fdls_send_fabric_abts(iport);
+		else
+			/* ABTS has timed out (2*ra_tov) */
+			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
+		break;
+	case FDLS_STATE_SCR:
+		/* 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_scr(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 (2*ra_tov), we give up */
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "ABTS timed out. Starting PLOGI: %p", iport);
+			fnic_fdls_start_plogi(iport);
+		}
+		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 (2*ra_tov), we give up */
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "ABTS timed out. Starting PLOGI: %p", iport);
+			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
+		}
+		break;
+	case FDLS_STATE_REGISTER_FC4_FEATURES:
+		/* scr received a LS_RJT with busy we retry from here */
+		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_register_fc4_features(iport);
+		} 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 (2*ra_tov), we give up */
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "ABTS timed out. Starting PLOGI %p", iport);
+			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
+		}
+		break;
+	case FDLS_STATE_RSCN_GPN_FT:
+	case FDLS_STATE_SEND_GPNFT:
+	case FDLS_STATE_GPN_FT:
+		/* GPN_FT received a LS_RJT with busy we retry from here */
+		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_gpn_ft(iport, iport->fabric.state);
+		} 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 have waited (2*ra_tov) can
+			 * retry safely with same exchange id
+			 */
+			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
+				fdls_send_gpn_ft(iport, iport->fabric.state);
+			} else {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
+					 iport);
+			}
+		}
+		break;
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
+{
+	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+	struct fnic *fnic = iport->fnic;
+	struct fnic_tport_event_s *tport_del_evt;
+
+	if (!IS_FNIC_FCP_INITIATOR(fnic))
+		return;
+
+	tport_del_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+	if (!tport_del_evt) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Failed to allocate memory for tport event fcid: 0x%x",
+			 tport->fcid);
+		return;
+	}
+	tport_del_evt->event = TGT_EV_TPORT_DELETE;
+	tport_del_evt->arg1 = (void *) tport;
+	list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+	queue_work(fnic_event_queue, &fnic->tport_work);
+}
+
+static void fdls_tport_timer_callback(struct timer_list *t)
+{
+	struct fnic_tport_s *tport = from_timer(tport, t, retry_timer);
+	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+	struct fnic *fnic = iport->fnic;
+	uint16_t oxid;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	if (!tport->timer_pending) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if (iport->state != FNIC_IPORT_STATE_READY) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if (tport->del_timer_inprogress) {
+		tport->del_timer_inprogress = 0;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n",
+			 tport->fcid);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		 "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d",
+		 tport->fcid, tport->timer_pending, tport->state,
+		 tport->retry_counter);
+
+	tport->timer_pending = 0;
+	oxid = ntohs(tport->oxid_used);
+
+	/* We retry plogi/prli/adisc frames depending on the tport state */
+	switch (tport->state) {
+	case FDLS_TGT_STATE_PLOGI:
+		/* PLOGI frame received a LS_RJT with busy, we retry from here */
+		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+			&& (tport->retry_counter < iport->max_plogi_retries)) {
+			fdls_free_tgt_oxid(iport, oxid);
+			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_tgt_plogi(iport, tport);
+		} else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+			/* Plogi frame has timed out 2*ed_tov send abts */
+			fdls_send_tport_abts(iport, tport);
+		} else if (tport->retry_counter < iport->max_plogi_retries) {
+			/*
+			 * ABTS has timed out have waited (2*ra_tov)
+			 * can retry safely
+			 * even if with same exchange id
+			 */
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_send_tgt_plogi(iport, tport);
+		} else {
+			/* exceeded plogi retry count */
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_send_delete_tport_msg(tport);
+		}
+		break;
+	case FDLS_TGT_STATE_PRLI:
+		/* PRLI received a LS_RJT with busy , hence we retry from here */
+		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
+			fdls_free_tgt_oxid(iport, oxid);
+			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_tgt_prli(iport, tport);
+		} else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+			/* PRLI has time out 2*ed_tov send abts */
+			fdls_send_tport_abts(iport, tport);
+		} else {
+			/* ABTS has timed out for prli, we go back to PLOGI */
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_send_tgt_plogi(iport, tport);
+			fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+		}
+		break;
+	case FDLS_TGT_STATE_ADISC:
+		/* ADISC timed out send a ABTS */
+		if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+			fdls_send_tport_abts(iport, tport);
+		} else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)
+				   && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+			/*
+			 * ABTS has timed out have waited (2*ra_tov) can
+			 * retry safely even if with same exchange id
+			 */
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_send_tgt_adisc(iport, tport);
+		} else {
+			/* exceeded retry count */
+			fdls_free_tgt_oxid(iport, oxid);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "ADISC not responding. Deleting target port: 0x%x",
+					 tport->fcid);
+			fdls_send_delete_tport_msg(tport);
+		}
+		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_hdr_s *fchdr)
+{
+	uint32_t tgt_fcid;
+	struct fnic_tport_s *tport;
+	uint8_t *fcid;
+	uint64_t frame_wwnn;
+	uint64_t frame_wwpn;
+	uint16_t oxid;
+	struct fc_els_adisc_ls_acc_s *adisc_rsp =
+		(struct fc_els_adisc_ls_acc_s *) fchdr;
+	struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
+	struct fnic *fnic = iport->fnic;
+
+	fcid = FNIC_GET_S_ID(fchdr);
+	tgt_fcid = ntoh24(fcid);
+	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Tgt ADISC response tport not found: 0x%x", tgt_fcid);
+		return;
+	}
+	if ((iport->state != FNIC_IPORT_STATE_READY)
+		|| (tport->state != FDLS_TGT_STATE_ADISC)
+		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Dropping this ADISC response");
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "iport state: %d tport state: %d Is abort issued on PRLI? %d",
+			 iport->state, tport->state,
+			 (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED));
+		return;
+	}
+	if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Dropping frame from target: 0x%x",
+			 tgt_fcid);
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery");
+		return;
+	}
+
+	switch (adisc_rsp->command) {
+	case 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;
+		oxid = ntohs(fchdr->ox_id);
+		fdls_free_tgt_oxid(iport, oxid);
+		frame_wwnn = get_unaligned_be64(&adisc_rsp->node_name);
+		frame_wwpn = get_unaligned_be64(&adisc_rsp->nport_name);
+		if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "ADISC accepted from target: 0x%x. Target logged in",
+				 tgt_fcid);
+			fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+		} else {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "Error mismatch frame: ADISC");
+		}
+		break;
+
+	case ELS_LS_RJT:
+		if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
+			 || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
+			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "ADISC ret 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);
+			oxid = ntohs(fchdr->ox_id);
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_delete_tport(iport, tport);
+		}
+		break;
+	}
+}
+
+
+static void
+fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
+						   struct fc_hdr_s *fchdr)
+{
+	uint32_t tgt_fcid;
+	struct fnic_tport_s *tport;
+	uint8_t *fcid;
+	uint16_t oxid;
+	struct fc_els_s *plogi_rsp = (struct fc_els_s *) fchdr;
+	struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
+	int max_payload_size;
+	struct fnic *fnic = iport->fnic;
+
+	fcid = FNIC_GET_S_ID(fchdr);
+	tgt_fcid = ntoh24(fcid);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS processing target PLOGI response: tgt_fcid: 0x%x",
+				 tgt_fcid);
+
+	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport not found: 0x%x", tgt_fcid);
+		return;
+	}
+	if ((iport->state != FNIC_IPORT_STATE_READY)
+		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Dropping frame! iport state: %d tport state: %d",
+					 iport->state, tport->state);
+		return;
+	}
+
+	if (tport->state != FDLS_TGT_STATE_PLOGI) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "PLOGI rsp recvd in wrong state. Restarting nexus");
+		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+		fdls_free_tgt_oxid(iport, oxid);
+		fdls_target_restart_nexus(tport);
+		return;
+	}
+
+	if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "PLOGI response from target: 0x%x. Dropping frame",
+			 tgt_fcid);
+		return;
+	}
+
+	switch (plogi_rsp->command) {
+	case ELS_LS_ACC:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PLOGI accepted by target: 0x%x", tgt_fcid);
+		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+		fdls_free_tgt_oxid(iport, oxid);
+		break;
+
+	case ELS_LS_RJT:
+		if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
+			 || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
+			&& (tport->retry_counter < iport->max_plogi_retries)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "PLOGI ret 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);
+		oxid = ntohs(fchdr->ox_id);
+		fdls_free_tgt_oxid(iport, oxid);
+		fdls_delete_tport(iport, tport);
+		return;
+
+	default:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PLOGI not accepted from target fcid: 0x%x",
+					 tgt_fcid);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Found the PLOGI target: 0x%x and state: %d",
+				 (unsigned int) tgt_fcid, tport->state);
+
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport fcid 0x%x: Canceling disc timer\n",
+					 tport->fcid);
+		fnic_del_tport_timer_sync(fnic, tport);
+	}
+
+	tport->timer_pending = 0;
+	tport->wwpn = get_unaligned_be64(&plogi_rsp->nport_name);
+	tport->wwnn = get_unaligned_be64(&plogi_rsp->node_name);
+
+	/* Learn the Service Params */
+
+	/* Max frame size - choose the lowest */
+	max_payload_size = fnic_fc_plogi_rsp_rdf(iport, plogi_rsp);
+	tport->max_payload_size =
+		MIN(max_payload_size, iport->max_payload_size);
+
+	if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "MFS: tport max frame size below spec bounds: %d",
+			 tport->max_payload_size);
+		tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		 "MAX frame size: %d iport max_payload_size: %d tport mfs: %d",
+		 max_payload_size, iport->max_payload_size,
+		 tport->max_payload_size);
+
+	tport->max_concur_seqs = FNIC_FC_PLOGI_RSP_CONCUR_SEQ(plogi_rsp);
+
+	tport->retry_counter = 0;
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_PRLI);
+	fdls_send_tgt_prli(iport, tport);
+}
+
+static void
+fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
+						  struct fc_hdr_s *fchdr)
+{
+	uint32_t tgt_fcid;
+	struct fnic_tport_s *tport;
+	uint8_t *fcid;
+	uint16_t oxid;
+	struct fc_els_prli_s *prli_rsp = (struct fc_els_prli_s *) fchdr;
+	struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
+	struct fnic_tport_event_s *tport_add_evt;
+	struct fnic *fnic = iport->fnic;
+	bool mismatched_tgt = false;
+
+	fcid = FNIC_GET_S_ID(fchdr);
+	tgt_fcid = ntoh24(fcid);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS process tgt PRLI response: 0x%x", tgt_fcid);
+
+	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport not found: 0x%x", tgt_fcid);
+		/* Handle or just drop? */
+		return;
+	}
+
+	if ((iport->state != FNIC_IPORT_STATE_READY)
+		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x",
+			 iport->state, tport->state, tport->fcid);
+		return;
+	}
+
+	if (tport->state != FDLS_TGT_STATE_PRLI) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PRLI rsp recvd in wrong state. Restarting nexus");
+		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+		fdls_free_tgt_oxid(iport, oxid);
+		fdls_target_restart_nexus(tport);
+		return;
+	}
+
+	if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Dropping PRLI response from target: 0x%x ",
+			 tgt_fcid);
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery");
+		return;
+	}
+
+	switch (prli_rsp->command) {
+	case ELS_LS_ACC:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PRLI accepted from target: 0x%x", tgt_fcid);
+		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+		fdls_free_tgt_oxid(iport, oxid);
+
+		if (prli_rsp->sp.type != FC_FC4_TYPE_SCSI) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "mismatched target zoned with FC SCSI initiator: 0x%x",
+				 tgt_fcid);
+			mismatched_tgt = true;
+		}
+		if (mismatched_tgt) {
+			fdls_tgt_logout(iport, tport);
+			fdls_delete_tport(iport, tport);
 			return;
 		}
-		/* RPN have timed out send abts */
-		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
-			fdls_send_fabric_abts(iport);
-		else
-			/* ABTS has timed out (2*ra_tov) */
-			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
 		break;
-	case FDLS_STATE_SCR:
-		/* 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_scr(iport);
-			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			return;
-		}
-		/* scr have timed out send abts */
-		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
-			fdls_send_fabric_abts(iport);
-		else {
-			/* ABTS has timed out (2*ra_tov), we give up */
+
+	case ELS_LS_RJT:
+		if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
+			 || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
+			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
+
 			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-						 "ABTS timed out. Starting PLOGI: %p", iport);
-			fnic_fdls_start_plogi(iport);
-		}
-		break;
-	case FDLS_STATE_REGISTER_FC4_TYPES:
-		/* scr received a LS_RJT with busy we retry from here */
-		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
-			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
-			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
-			fdls_send_register_fc4_types(iport);
-			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+				 "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;
-		}
-		/* RFT_ID timed out send abts */
-		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
-			fdls_send_fabric_abts(iport);
 		} else {
-			/* ABTS has timed out (2*ra_tov), we give up */
-			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-						 "ABTS timed out. Starting PLOGI: %p", iport);
-			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
-		}
-		break;
-	case FDLS_STATE_REGISTER_FC4_FEATURES:
-		/* scr received a LS_RJT with busy we retry from here */
-		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
-			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
-			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
-			fdls_send_register_fc4_features(iport);
-			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			return;
-		}
-		/* scr have timed out send abts */
-		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
-			fdls_send_fabric_abts(iport);
-		else {
-			/* ABTS has timed out (2*ra_tov), we give up */
 			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-						 "ABTS timed out. Starting PLOGI %p", iport);
-			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
-		}
-		break;
-	case FDLS_STATE_RSCN_GPN_FT:
-	case FDLS_STATE_SEND_GPNFT:
-	case FDLS_STATE_GPN_FT:
-		/* GPN_FT received a LS_RJT with busy we retry from here */
-		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
-			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
-			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
-			fdls_send_gpn_ft(iport, iport->fabric.state);
-			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+						 "PRLI returned ELS_LS_RJT from target: 0x%x",
+						 tgt_fcid);
+
+			oxid = ntohs(fchdr->ox_id);
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_tgt_logout(iport, tport);
+			fdls_delete_tport(iport, tport);
 			return;
 		}
-		/* gpn_gt have timed out send abts */
-		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
-			fdls_send_fabric_abts(iport);
-		} else {
-			/*
-			 * ABTS has timed out have waited (2*ra_tov) can
-			 * retry safely with same exchange id
-			 */
-			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
-				fdls_send_gpn_ft(iport, iport->fabric.state);
-			} else {
-				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
-					 iport);
-			}
-		}
 		break;
+
 	default:
+		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;
+
+	oxid = ntohs(FNIC_GET_OX_ID(fchdr));
+	fdls_free_tgt_oxid(iport, oxid);
+
+	/* Learn Service Params */
+	tport->fcp_csp = ntohl(prli_rsp->sp.csp);
+	tport->retry_counter = 0;
+
+	if (prli_rsp->sp.csp & FCP_SPPF_RETRY)
+		tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
+
+	/* Check if the device plays Target Mode Function */
+	if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Remote port(0x%x): no target support. Deleting it\n",
+			 tgt_fcid);
+		fdls_tgt_logout(iport, tport);
+		fdls_delete_tport(iport, tport);
+		return;
+	}
+
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+
+	/* Inform the driver about new target added */
+	tport_add_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+	if (!tport_add_evt) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport event memory allocation failure: 0x%0x\n",
+				 tport->fcid);
+		return;
+	}
+	tport_add_evt->event = TGT_EV_RPORT_ADD;
+	tport_add_evt->arg1 = (void *) tport;
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "iport fcid: 0x%x add tport event fcid: 0x%x\n",
+			 tport->fcid, iport->fcid);
+	list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
+	queue_work(fnic_event_queue, &fnic->tport_work);
 }
 
+
 static void
 fdls_process_rff_id_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr)
 {
@@ -892,6 +1886,106 @@  fdls_process_scr_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr)
 	}
 }
 
+static void
+fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
+							 struct fc_hdr_s *fchdr, int len)
+{
+	struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt;
+	struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt_rem;
+	struct fnic_tport_s *tport, *next;
+	uint32_t fcid;
+	uint64_t wwpn;
+	int rem_len = len;
+	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
+
+	gpn_ft_tgt =
+		(struct fc_gpn_ft_rsp_iu_s *) ((uint8_t *) fchdr +
+								   sizeof(struct fc_hdr_s)
+								   + sizeof(struct fc_ct_hdr_s));
+	gpn_ft_tgt_rem = gpn_ft_tgt;
+	len -= sizeof(struct fc_hdr_s) + sizeof(struct fc_ct_hdr_s);
+
+	while (rem_len > 0) {
+
+		fcid = ntoh24(gpn_ft_tgt->fcid);
+		wwpn = ntohll(gpn_ft_tgt->wwpn);
+
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
+
+		if (fcid == iport->fcid) {
+			if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
+				break;
+			gpn_ft_tgt++;
+			rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
+			continue;
+		}
+
+		tport = fnic_find_tport_by_wwpn(iport, wwpn);
+		if (!tport) {
+			/*
+			 * New port registered with the switch or first time query
+			 */
+			tport = fdls_create_tport(iport, fcid, wwpn);
+			if (!tport)
+				return;
+		}
+		/*
+		 * check if this was an existing tport with same fcid
+		 * but whose wwpn has changed now ,then remove it and
+		 * create a new one
+		 */
+		if (tport->fcid != fcid) {
+			fdls_delete_tport(iport, tport);
+			tport = fdls_create_tport(iport, fcid, wwpn);
+			if (!tport)
+				return;
+		}
+
+		/*
+		 * If this GPN_FT rsp is after RSCN then mark the tports which
+		 * matches with the new GPN_FT list, if some tport is not
+		 * found in GPN_FT we went to delete that tport later.
+		 */
+		if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT)
+			tport->flags |= FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+
+		if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
+			break;
+
+		gpn_ft_tgt++;
+		rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
+	}
+	if (rem_len <= 0) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
+			 len, rem_len);
+	}
+
+	/*remove those ports which was not listed in GPN_FT */
+	if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT) {
+		list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+
+			if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Remove port: 0x%x not found in GPN_FT list",
+					 tport->fcid);
+				fdls_delete_tport(iport, tport);
+			} else {
+				tport->flags &= ~FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+			}
+			if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+				|| (iport->state != FNIC_IPORT_STATE_READY)) {
+				return;
+			}
+		}
+	}
+}
+
 static void
 fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
 						int len)
@@ -900,6 +1994,9 @@  fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
 	struct fc_gpn_ft_s *gpn_ft_rsp = (struct fc_gpn_ft_s *) fchdr;
 	uint16_t rsp;
 	uint8_t reason_code;
+	int count = 0;
+	struct fnic_tport_s *tport, *next;
+	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
 	struct fnic *fnic = iport->fnic;
 
 	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
@@ -936,12 +2033,74 @@  fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
 	case FC_CT_ACC:
 		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
 					 "0x%x: GPNFT_RSP accept", iport->fcid);
+		if (iport->fabric.timer_pending) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "0x%x: Canceling fabric disc timer\n",
+						 iport->fcid);
+			fnic_del_fabric_timer_sync(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_CT_REJ:
 		reason_code = gpn_ft_rsp->fc_ct_hdr.reason_code;
 		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
 					 "0x%x: GPNFT_RSP Reject", iport->fcid);
+
+		if (((reason_code == FC_CT_RJT_LOGICAL_BUSY)
+			 || (reason_code == FC_CT_RJT_BUSY))
+			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine",
+				 iport->fcid);
+			/* Retry again from the timer routine */
+			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+		} else {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "0x%x: GPNFT_RSP reject", iport->fcid);
+			if (iport->fabric.timer_pending) {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+							 "0x%x: Canceling fabric disc timer\n",
+							 iport->fcid);
+				fnic_del_fabric_timer_sync(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:
@@ -1342,6 +2501,141 @@  fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 	}
 }
 
+static void
+fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
+						  struct fc_hdr_s *fchdr)
+{
+	uint32_t s_id;
+	struct fnic_tport_s *tport;
+	uint32_t tport_state;
+	struct fc_abts_ba_acc_s *ba_acc;
+	struct fc_abts_ba_rjt_s *ba_rjt;
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+
+	s_id = ntoh24(fchdr->sid);
+	ba_acc = (struct fc_abts_ba_acc_s *) fchdr;
+	ba_rjt = (struct fc_abts_ba_rjt_s *) fchdr;
+
+	tport = fnic_find_tport_by_fcid(iport, s_id);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+					 "Received tgt abts rsp with invalid SID: 0x%x", s_id);
+		return;
+	}
+
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+					 "tport 0x%p Canceling fabric disc timer\n", tport);
+		fnic_del_tport_timer_sync(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->ox_id);
+
+	/*This abort rsp is for ADISC */
+	if ((oxid >= FDLS_ADISC_OXID_BASE) && (oxid < FDLS_TGT_OXID_POOL_END)) {
+		if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+						 "Received tgt ADISC abts response BA_ACC for OX_ID: 0x%x tgt_fcid: 0x%x",
+						 ba_acc->ox_id, tport->fcid);
+		} else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+				 "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ",
+				 tport->fcid, tport_state);
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+				 "reason code: 0x%x reason code explanation:0x%x ",
+				 ba_rjt->reason_code, ba_rjt->reason_explanation);
+		}
+		if ((tport->retry_counter < FDLS_RETRY_COUNT)
+			&& (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_send_tgt_adisc(iport, tport);
+			return;
+		}
+		fdls_free_tgt_oxid(iport, oxid);
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+					 "ADISC not responding. Deleting target port: 0x%x",
+					 tport->fcid);
+		fdls_delete_tport(iport, tport);
+		if ((iport->state == FNIC_IPORT_STATE_READY)
+			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+		}
+		/*Restart a discovery of targets */
+		return;
+	}
+
+	/*This abort rsp is for PLOGI */
+	if ((oxid >= FDLS_PLOGI_OXID_BASE) && (oxid < FDLS_PRLI_OXID_BASE)) {
+		if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+				 "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x",
+				 tport->fcid);
+		} else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x",
+				 tport->fcid, fchdr->ox_id);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "reason code: 0x%x reason code explanation: 0x%x",
+				  ba_rjt->reason_code, ba_rjt->reason_explanation);
+		}
+		if ((tport->retry_counter < iport->max_plogi_retries)
+			&& (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_send_tgt_plogi(iport, tport);
+			return;
+		}
+		fdls_free_tgt_oxid(iport, oxid);
+		fdls_delete_tport(iport, tport);
+		/*Restart a discovery of targets */
+		if ((iport->state == FNIC_IPORT_STATE_READY)
+			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+		}
+		return;
+	}
+
+	/*This abort rsp is for PRLI */
+	if ((oxid >= FDLS_PRLI_OXID_BASE) && (oxid < FDLS_ADISC_OXID_BASE)) {
+		if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: Received tgt PRLI abts response BA_ACC",
+				 tport->fcid);
+		} else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ",
+				 tport->fcid, fchdr->ox_id);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "reason code: 0x%x reason code explanation: 0x%x",
+				 ba_rjt->reason_code,
+				 ba_rjt->reason_explanation);
+		}
+		if ((tport->retry_counter < FDLS_RETRY_COUNT)
+			&& (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
+			fdls_free_tgt_oxid(iport, oxid);
+			fdls_send_tgt_prli(iport, tport);
+			return;
+		}
+		fdls_free_tgt_oxid(iport, oxid);
+		fdls_send_tgt_plogi(iport, tport);	/* go back to plogi */
+		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Received ABTS response for unknown frame %p", iport);
+}
+
 /*
  * Performs a validation for all FCOE frames and return the frame type
  */
@@ -1434,6 +2728,42 @@  fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 		}
 	}
 
+	/* ELS response from a target */
+	if ((ntohs(oxid) >= FDLS_PLOGI_OXID_BASE)
+		&& (ntohs(oxid) < FDLS_PRLI_OXID_BASE)) {
+		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"Dropping Unknown frame in PLOGI exchange range type: 0x%x.",
+				fchdr->type);
+			return -1;
+		}
+		return FNIC_TPORT_PLOGI_RSP;
+	}
+	if ((ntohs(oxid) >= FDLS_PRLI_OXID_BASE)
+		&& (ntohs(oxid) < FDLS_ADISC_OXID_BASE)) {
+		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"Dropping Unknown frame in PRLI exchange range type: 0x%x.",
+				fchdr->type);
+			return -1;
+		}
+		return FNIC_TPORT_PRLI_RSP;
+	}
+
+	if ((ntohs(oxid) >= FDLS_ADISC_OXID_BASE)
+		&& (ntohs(oxid) < FDLS_TGT_OXID_POOL_END)) {
+		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"Dropping Unknown frame in ADISC exchange range type: 0x%x.",
+				fchdr->type);
+			return -1;
+		}
+		return FNIC_TPORT_ADISC_RSP;
+	}
+	if (ntohs(oxid) == FNIC_TLOGO_REQ_OXID) {
+		return FNIC_TPORT_LOGO_RSP;
+	}
+
 	/*response from fabric */
 	switch (oxid) {
 
@@ -1557,6 +2887,21 @@  void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 	case FNIC_FABRIC_GPN_FT_RSP:
 		fdls_process_gpn_ft_rsp(iport, fchdr, len);
 		break;
+	case FNIC_TPORT_PLOGI_RSP:
+		fdls_process_tgt_plogi_rsp(iport, fchdr);
+		break;
+	case FNIC_TPORT_PRLI_RSP:
+		fdls_process_tgt_prli_rsp(iport, fchdr);
+		break;
+	case FNIC_TPORT_ADISC_RSP:
+		fdls_process_tgt_adisc_rsp(iport, fchdr);
+		break;
+	case FNIC_TPORT_LOGO_RSP:
+		/* Logo response from tgt which we have deleted */
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Logo response from tgt: 0x%x",
+					 ntoh24(fchdr->sid));
+		break;
 	case FNIC_FABRIC_LOGO_RSP:
 		fdls_process_fabric_logo_rsp(iport, fchdr);
 		break;
@@ -1566,7 +2911,8 @@  void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 		if ((iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)
 			&& (oxid >= FNIC_FLOGI_OXID && oxid <= FNIC_RFF_REQ_OXID)) {
 			fdls_process_fabric_abts_rsp(iport, fchdr);
-		}
+		} else
+			fdls_process_tgt_abts_rsp(iport, fchdr);
 		break;
 	default:
 		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 2d5f438f2cc4..92cd17efa40f 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -79,6 +79,9 @@ 
 
 #define IS_FNIC_FCP_INITIATOR(fnic) (fnic->role == FNIC_ROLE_FCP_INITIATOR)
 
+/* Retry supported by rport (returned by PRLI service parameters) */
+#define FNIC_FC_RP_FLAGS_RETRY            0x1
+
 /*
  * fnic private data per SCSI command.
  * These fields are locked by the hashed io_req_lock.
@@ -133,6 +136,7 @@  static inline u64 fnic_flags_and_state(struct scsi_cmnd *cmd)
 
 extern unsigned int fnic_log_level;
 extern unsigned int io_completions;
+extern struct workqueue_struct *fnic_event_queue;
 
 #define FNIC_MAIN_LOGGING 0x01
 #define FNIC_FCS_LOGGING 0x02
@@ -329,6 +333,8 @@  struct fnic {
 	struct work_struct flush_work;
 	struct sk_buff_head frame_queue;
 	struct list_head tx_queue;
+	struct work_struct tport_work;
+	struct list_head tport_event_list;
 
 	/*** FIP related data members  -- start ***/
 	void (*set_vlan)(struct fnic *, u16 vlan);
diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
index f64a2ba2e10d..bb3e9d26c18a 100644
--- a/drivers/scsi/fnic/fnic_fdls.h
+++ b/drivers/scsi/fnic/fnic_fdls.h
@@ -312,6 +312,10 @@  void fdls_send_fabric_logo(struct fnic_iport_s *iport);
 int fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 								  void *rx_frame, int len,
 								  int fchdr_offset);
+void fdls_send_tport_abts(struct fnic_iport_s *iport,
+						  struct fnic_tport_s *tport);
+void fdls_delete_tport(struct fnic_iport_s *iport,
+					   struct fnic_tport_s *tport);
 void fdls_fdmi_timer_callback(struct timer_list *t);
 
 /* fnic_fcs.c */