diff mbox series

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

Message ID 20240610215100.673158-5-kartilak@cisco.com (mailing list archive)
State Changes Requested
Headers show
Series Introduce support for feature Fabric Discovery and | expand

Commit Message

Karan Tilak Kumar (kartilak) June 10, 2024, 9:50 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>
---
 drivers/scsi/fnic/fdls_disc.c | 1540 +++++++++++++++++++++++++++++++--
 drivers/scsi/fnic/fnic.h      |    6 +
 drivers/scsi/fnic/fnic_fdls.h |    4 +
 3 files changed, 1473 insertions(+), 77 deletions(-)

Comments

Hannes Reinecke June 11, 2024, 1:31 p.m. UTC | #1
On 6/10/24 23:50, 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.
> 
> 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>
> ---
>   drivers/scsi/fnic/fdls_disc.c | 1540 +++++++++++++++++++++++++++++++--
>   drivers/scsi/fnic/fnic.h      |    6 +
>   drivers/scsi/fnic/fnic_fdls.h |    4 +
>   3 files changed, 1473 insertions(+), 77 deletions(-)
> 
> diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
> index 22a2d0c1c78f..d920202d413d 100644
> --- a/drivers/scsi/fnic/fdls_disc.c
> +++ b/drivers/scsi/fnic/fdls_disc.c
> @@ -11,6 +11,8 @@
>   #include <scsi/fc/fc_fcp.h>
>   #include <linux/utsname.h>
>   
> +#define FC_FC4_TYPE_SCSI 0x08
> +
>   static void fdls_send_rpn_id(struct fnic_iport_s *iport);
>   
>   /* Frame initialization */
> @@ -59,6 +61,19 @@ struct fc_rpn_id_s fnic_rpn_id_req = {
>   				  .command = FC_CT_RPN_CMD}
>   };
>   
> +/*
> + * Variables:
> + * did, sid, oxid
> + */
> +struct fc_els_prli_s fnic_prli_req = {
> +	.fchdr = {.r_ctl = 0x22, .type = 0x01,
> +			  .f_ctl = FNIC_ELS_REQ_FCTL, .rx_id = 0xFFFF},
> +	.command = FC_ELS_PRLI_REQ,
> +	.page_len = 16,
> +	.payload_len = 0x1400,
> +	.sp = {.type = 0x08, .flags = 0x0020, .csp = 0xA2000000}
> +};
> +
>   /*
>    * Variables:
>    * fh_s_id, port_id, port_name
> @@ -123,11 +138,51 @@ struct fc_logo_req_s fnic_logo_req = {
>   #define RETRIES_EXHAUSTED(iport)      \
>   	(iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)
>   
> +#define FNIC_TPORT_MAX_NEXUS_RESTART (8)
> +
> +/* Private Functions */
>   static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
>   		   struct fc_hdr_s *fchdr, void *rx_frame);
>   static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
> +static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
> +									  uint32_t fcid,
> +									  uint64_t wwpn);
> +static void fdls_target_restart_nexus(struct fnic_tport_s *tport);
> +static void fdls_start_tport_timer(struct fnic_iport_s *iport,
> +					struct fnic_tport_s *tport, int timeout);
> +
>   static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
>   			int timeout);
> +static void fdls_tport_timer_callback(struct timer_list *t);
> +
> +static uint16_t fdls_alloc_tgt_oxid(struct fnic_iport_s *iport,
> +									uint16_t base)
> +{
> +	int i;
> +	int start, end;
> +
> +	start = base - FDLS_PLOGI_OXID_BASE;
> +	end = start + FDLS_TGT_OXID_BLOCK_SZ;
> +
> +	for (i = start; i < end; i++) {
> +		if (iport->tgt_oxid_pool[i] == 0) {
> +			iport->tgt_oxid_pool[i] = 1;
> +			return (i + FDLS_PLOGI_OXID_BASE);
> +		}
> +	}
> +	return 0xFFFF;
> +}
> +
> +static void fdls_free_tgt_oxid(struct fnic_iport_s *iport, uint16_t oxid)
> +{
> +	struct fnic *fnic = iport->fnic;
> +
> +	if (iport->tgt_oxid_pool[oxid - FDLS_PLOGI_OXID_BASE] != 1) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Freeing unused OXID: 0x%x", oxid);
> +	}
> +	iport->tgt_oxid_pool[oxid - FDLS_PLOGI_OXID_BASE] = 0;
> +}
>   
Better use a bitmap here; fdls_alloc_tgt_oxid() is not atomic, and will 
need to be locked to avoid races.

>   static void
>   fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
> @@ -153,6 +208,63 @@ fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
>   				 "fabric timer is %d ", timeout);
>   }
>   
> +static void
> +fdls_start_tport_timer(struct fnic_iport_s *iport,
> +					   struct fnic_tport_s *tport, int timeout)
> +{
> +	u64 fabric_tov;
> +	struct fnic *fnic = iport->fnic;
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync();
> +		tport->timer_pending = 0;
> +	}
> +
> +	if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED))
> +		tport->retry_counter++;
> +
> +	fabric_tov = jiffies + msecs_to_jiffies(timeout);
> +	mod_timer(&tport->retry_timer, round_jiffies(fabric_tov));
> +	tport->timer_pending = 1;
> +}
> +
> +void
> +fdls_send_tport_abts(struct fnic_iport_s *iport,
> +					 struct fnic_tport_s *tport)
> +{
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	struct fnic *fnic = iport->fnic;
> +
> +	struct fc_hdr_s fc_abts_s = {
> +		.r_ctl = 0x81,			/* ABTS */
> +		.cs_ctl = 0x00, .type = 0x00, .f_ctl = FNIC_REQ_ABTS_FCTL, .seq_id =
> +			0x00, .df_ctl = 0x00, .seq_cnt = 0x0000, .rx_id = 0xFFFF,
> +		.param = 0x00000000,	/* bit:0 = 0 Abort a exchange */
> +	};
Why do you define this as a local variable, but frames as globals?
Please make all of these frames global.

> +
> +	struct fc_hdr_s *pfc_abts = &fc_abts_s;
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +	FNIC_SET_S_ID(pfc_abts, s_id);
> +	FNIC_SET_D_ID(pfc_abts, d_id);
> +	tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
> +
> +	fc_abts_s.ox_id = tport->oxid_used;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS sending tport abts: tport->state: %d ",
> +				 tport->state);
> +
> +	fnic_send_fcoe_frame(iport, &fc_abts_s, sizeof(struct fc_hdr_s));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, 2 * iport->r_a_tov);
> +}
> +
>   static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
>   {
>   	uint8_t fcid[3];
> @@ -338,6 +450,169 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
>   	fdls_set_state((&iport->fabric), fdls_state);
>   }
>   
> +static void
> +fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fc_els_adisc_s adisc;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +
> +	memset(&adisc, 0, sizeof(struct fc_els_adisc_s));
> +	adisc.fchdr.r_ctl = 0x22;
> +	adisc.fchdr.type = 0x01;
> +	adisc.fchdr.f_ctl = FNIC_ELS_REQ_FCTL;
> +	adisc.fchdr.rx_id = 0xFFFF;
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +	FNIC_SET_S_ID((&adisc.fchdr), s_id);
> +	FNIC_SET_D_ID((&adisc.fchdr), d_id);
> +
> +	oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_ADISC_OXID_BASE));
> +	if (oxid == 0xFFFF) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Failed to allocate OXID to send ADISC %p", iport);
> +		return;
> +	}
> +
> +	tport->oxid_used = oxid;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +
> +	FNIC_SET_OX_ID((&adisc.fchdr), oxid);
> +	FNIC_SET_NPORT_NAME(adisc, iport->wwpn);
> +	FNIC_SET_NODE_NAME(adisc, iport->wwnn);
> +
> +	memcpy(adisc.fcid, s_id, 3);
> +	adisc.command = FNIC_ELS_ADISC_REQ;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "sending ADISC to tgt fcid: 0x%x", tport->fcid);
> +
> +	fnic_send_fcoe_frame(iport, &adisc, sizeof(struct fc_els_adisc_s));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);

How do you recover the oxid if fnic_send_fcoe_frame() fails?

> +}
> +
> +void fdls_delete_tport(struct fnic_iport_s *iport,
> +					   struct fnic_tport_s *tport)
> +{
> +	struct fnic_tport_event_s *tport_del_evt;
> +	struct fnic *fnic = iport->fnic;
> +
> +	if ((tport->state == FDLS_TGT_STATE_OFFLINING)
> +		|| (tport->state == FDLS_TGT_STATE_OFFLINE))
> +		return;
> +
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
> +	/*
> +	 * By setting this flag, the tport will not be seen in a look-up
> +	 * in an RSCN. Even if we move to multithreaded model, this tport
> +	 * will be destroyed and a new RSCN will have to create a new one
> +	 */
> +	tport->flags |= FNIC_FDLS_TPORT_TERMINATING;
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync();
> +		tport->timer_pending = 0;
> +	}
> +
> +	if (IS_FNIC_FCP_INITIATOR(fnic)) {
> +		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
> +		fnic_rport_exch_reset(iport->fnic, tport->fcid);
> +		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
> +
> +		if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) {
> +			tport_del_evt =
> +				kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
> +			if (!tport_del_evt) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Failed to allocate memory for tport fcid: 0x%0x\n",
> +					 tport->fcid);
> +				return;
> +			}
> +			tport_del_evt->event = TGT_EV_RPORT_DEL;
> +			tport_del_evt->arg1 = (void *) tport;
> +			list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
> +			queue_work(fnic_event_queue, &fnic->tport_work);
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "tport 0x%x not reg with scsi_transport. Freeing locally",
> +				 tport->fcid);
> +			list_del(&tport->links);
> +			kfree(tport);
> +		}
> +	}
> +}
> +
I'm slightly curious about locking here.
You do call spin_lock() etc when calling fnic_rport_exch_reset(), so
apparently this function runs lockless. But if it does, how do you 
ensure that 'tport->flags' is not modified after checking, causing
you do queue an event for a tport which is already unregistered?
Maybe you should look at reference counting for tports?

> +static void
> +fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fc_els_s plogi;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +	uint32_t timeout;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Send tgt PLOGI to fcid: 0x%x", tport->fcid);
> +
> +	memcpy(&plogi, &fnic_plogi_req, sizeof(struct fc_els_s));
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +
> +	FNIC_SET_S_ID((&plogi.fchdr), s_id);
> +	FNIC_SET_D_ID((&plogi.fchdr), d_id);
> +	FNIC_SET_RDF_SIZE(plogi.u.csp_plogi, iport->max_payload_size);
> +
> +	oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_PLOGI_OXID_BASE));
> +	if (oxid == 0xFFFF) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
> +				 iport->fcid, tport->fcid);
> +		return;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "send tgt PLOGI: tgt fcid: 0x%x oxid: 0x%x", tport->fcid,
> +				 ntohs(oxid));
> +	tport->oxid_used = oxid;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +
> +	FNIC_SET_OX_ID((&plogi.fchdr), oxid);
> +	FNIC_SET_NPORT_NAME(plogi, iport->wwpn);
> +	FNIC_SET_NODE_NAME(plogi, iport->wwnn);
> +
> +	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
> +
> +	fnic_send_fcoe_frame(iport, &plogi, sizeof(struct fc_els_s));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, timeout);
> +}
> +
> +static uint16_t
> +fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
> +					  struct fc_els_s *plogi_rsp)
> +{
> +	uint16_t b2b_rdf_size = ntohs(plogi_rsp->u.csp_plogi.b2b_rdf_size);
> +	uint16_t spc3_rdf_size =
> +		((uint16_t) (plogi_rsp->spc3[6] << 8 | plogi_rsp->spc3[7]) &
> +		 FNIC_FC_C3_RDF);
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
> +			 b2b_rdf_size, spc3_rdf_size);
> +
> +	return MIN(b2b_rdf_size, spc3_rdf_size);
> +}
> +
>   static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
>   {
>   	struct fc_rft_id rft_id;
> @@ -387,6 +662,47 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
>   	fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
>   }
>   
> +static void
> +fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fc_els_prli_s prli;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +	uint32_t timeout;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS sending PRLI to tgt: 0x%x", tport->fcid);
> +
> +	oxid = htons(fdls_alloc_tgt_oxid(iport, FDLS_PRLI_OXID_BASE));
> +	if (oxid == 0xFFFF) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Failed to allocate OXID to send PRLI %p", iport);
> +		return;
> +	}
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS sending PRLI to tgt: 0x%x OXID: 0x%x", tport->fcid,
> +				 ntohs(oxid));
> +
> +	tport->oxid_used = oxid;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +	memcpy(&prli, &fnic_prli_req, sizeof(struct fc_els_prli_s));
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +
> +	FNIC_SET_S_ID((&prli.fchdr), s_id);
> +	FNIC_SET_D_ID((&prli.fchdr), d_id);
> +	FNIC_SET_OX_ID((&prli.fchdr), oxid);
> +
> +	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
> +
> +	fnic_send_fcoe_frame(iport, &prli, sizeof(struct fc_els_prli_s));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, timeout);
> +}
> +
>   /***********************************************************************
>    * fdls_send_fabric_logo
>    *
> @@ -434,6 +750,208 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport)
>   	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_logo_req_s));
>   }
>   
> +/***********************************************************************
> + * fdls_tgt_logout
> + *
> + * \brief Send plogo to the remote port
> + *
> + * \param[in]  iport   Handle to fnic iport. remote port
> + *
> + * \retval void
> + *
> + * \note This function does not change or check the fabric/tport state.
> + *       It the caller responsibility to set the appropriate tport/fabric
> + *       state when this is called. Normall fdls_tgt_state_plogo.
> + *       fdls_set_tport_state(tport, fdls_tgt_state_plogo)
> + *
> + *\note This could be used to send plogo to nameserver process
> + *       also not just target processes
> + *
> + ***********************************************************************/

Please convert to valid kernel-doc format.

> +void fdls_tgt_logout(struct fnic_iport_s *iport,
> +					 struct fnic_tport_s *tport)
> +{
> +	struct fc_logo_req_s logo;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Sending logo to tport fcid: 0x%x", tport->fcid);
> +	memcpy(&logo, &fnic_logo_req, sizeof(struct fc_logo_req_s));
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +
> +	FNIC_SET_S_ID((&logo.fchdr), s_id);
> +	FNIC_SET_D_ID((&logo.fchdr), d_id);
> +	FNIC_SET_OX_ID((&logo.fchdr), FNIC_TLOGO_REQ_OXID);
> +
> +	memcpy(&logo.fcid, s_id, 3);
> +	logo.wwpn = htonll(iport->wwpn);
> +
> +	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_logo_req_s));
> +}
> +
> +static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
> +{
> +	struct fnic_tport_s *tport, *next;
> +	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: Starting FDLS target discovery", iport->fcid);
> +
> +	list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
> +		if ((old_link_down_cnt != iport->fnic->link_down_cnt)
> +			|| (iport->state != FNIC_IPORT_STATE_READY)) {
> +			break;
> +		}
> +		/* if we marked the tport as deleted due to GPN_FT
> +		 * We should not send ADISC anymore
> +		 */
> +		if ((tport->state == FDLS_TGT_STATE_OFFLINING) ||
> +			(tport->state == FDLS_TGT_STATE_OFFLINE))
> +			continue;

This check is not atomic, ensure that there are not side-effects.
Maybe check if the 'state' variable shouldn't be protected.

> +
> +		/* 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);

And this can't fail? Really?

> +
> +	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;
> +}
> +

Locking?
The _safe() variants protect against removal from the list, they do
_not_ protect against removal of the element itself.
Nor concurrent accesses to the same element.

> +static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
> +								  uint32_t fcid, uint64_t wwpn)
> +{
> +	struct fnic_tport_s *tport;
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);
> +
> +	tport = kzalloc(sizeof(struct fnic_tport_s), GFP_ATOMIC);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Memory allocation failure while creating tport: 0x%x\n",
> +			 fcid);
> +		return NULL;
> +	}
> +
> +	tport->max_payload_size = FNIC_FCOE_MAX_FRAME_SZ;
> +	tport->r_a_tov = FNIC_R_A_TOV_DEF;
> +	tport->e_d_tov = FNIC_E_D_TOV_DEF;
> +	tport->fcid = fcid;
> +	tport->wwpn = wwpn;
> +	tport->iport = iport;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Need to setup tport timer callback");
> +
> +	timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Added tport 0x%x", tport->fcid);
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
> +	list_add_tail(&tport->links, &iport->tport_list);
> +	atomic_set(&tport->in_flight, 0);
> +	return tport;
> +}
> +
> +struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
> +									 uint64_t wwpn)
> +{
> +	struct fnic_tport_s *tport, *next;
> +
> +	list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
> +		if ((tport->wwpn == wwpn)
> +			&& !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
> +			return tport;
> +	}
> +	return NULL;
> +}
> +
>   void fdls_fabric_timer_callback(struct timer_list *t)
>   {
>   	struct fnic_fdls_fabric_s *fabric = from_timer(fabric, t, retry_timer);
> @@ -548,98 +1066,614 @@ void fdls_fabric_timer_callback(struct timer_list *t)
>   		else {
>   			/* ABTS has timed out (2*ra_tov), we give up */
>   			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -						 "ABTS timed out. Starting PLOGI: %p", iport);
> -			fnic_fdls_start_plogi(iport);
> +						 "ABTS timed out. Starting PLOGI: %p", iport);
> +			fnic_fdls_start_plogi(iport);
> +		}
> +		break;
> +	case FDLS_STATE_REGISTER_FC4_TYPES:
> +		/* scr received a LS_RJT with busy we retry from here */
> +		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> +			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_register_fc4_types(iport);
> +			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +			return;
Please don't do returns here.
Convert it into if() {} else if() {} else {} clauses, and call 
spin_unlock only at the end of the switch.

> +		}
> +		/* 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;
Same here.

> +		}
> +		/* scr have timed out send abts */
> +		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
> +			fdls_send_fabric_abts(iport);
> +		else {
> +			/* ABTS has timed out (2*ra_tov), we give up */
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "ABTS timed out. Starting PLOGI %p", iport);
> +			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
> +		}
> +		break;
> +	case FDLS_STATE_RSCN_GPN_FT:
> +	case FDLS_STATE_SEND_GPNFT:
> +	case FDLS_STATE_GPN_FT:
> +		/* GPN_FT received a LS_RJT with busy we retry from here */
> +		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> +			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_gpn_ft(iport, iport->fabric.state);
> +			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +			return;
> +		}

And here.

> +		/* gpn_gt have timed out send abts */
> +		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
> +			fdls_send_fabric_abts(iport);
> +		} else {
> +			/*
> +			 * ABTS has timed out have waited (2*ra_tov) can
> +			 * retry safely with same exchange id
> +			 */
> +			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
> +				fdls_send_gpn_ft(iport, iport->fabric.state);
> +			} else {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
> +					 iport);
> +			}
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +}
> +
> +static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
> +{
> +	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
> +	struct fnic *fnic = iport->fnic;
> +	struct fnic_tport_event_s *tport_del_evt;
> +
> +	if (!IS_FNIC_FCP_INITIATOR(fnic))
> +		return;
> +
> +	tport_del_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
> +	if (!tport_del_evt) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Failed to allocate memory for tport event fcid: 0x%x",
> +			 tport->fcid);
> +		return;
> +	}
> +	tport_del_evt->event = TGT_EV_TPORT_DELETE;
> +	tport_del_evt->arg1 = (void *) tport;
> +	list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
> +	queue_work(fnic_event_queue, &fnic->tport_work);
> +}
> +
> +static void fdls_tport_timer_callback(struct timer_list *t)
> +{
> +	struct fnic_tport_s *tport = from_timer(tport, t, retry_timer);
> +	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
> +	struct fnic *fnic = iport->fnic;
> +	uint16_t oxid;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&fnic->fnic_lock, flags);
> +	if (!tport->timer_pending) {
> +		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		return;
> +	}
> +
> +	if (iport->state != FNIC_IPORT_STATE_READY) {
> +		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		return;
> +	}
> +
> +	if (tport->del_timer_inprogress) {
> +		tport->del_timer_inprogress = 0;
> +		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n",
> +			 tport->fcid);
> +		return;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +		 "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d",
> +		 tport->fcid, tport->timer_pending, tport->state,
> +		 tport->retry_counter);
> +
> +	tport->timer_pending = 0;
> +	oxid = ntohs(tport->oxid_used);
> +
> +	/* We retry plogi/prli/adisc frames depending on the tport state */
> +	switch (tport->state) {
> +	case FDLS_TGT_STATE_PLOGI:
> +		/* PLOGI frame received a LS_RJT with busy, we retry from here */
> +		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (tport->retry_counter < iport->max_plogi_retries)) {
> +			fdls_free_tgt_oxid(iport, oxid);
> +			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_tgt_plogi(iport, tport);
> +			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +			return;
Same here.

> +		}
> +		/* Plogi frame has timed out 2*ed_tov send abts */
> +		if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +			fdls_send_tport_abts(iport, tport);
> +		} else if (tport->retry_counter < iport->max_plogi_retries) {
> +			/*
> +			 * ABTS has timed out have waited (2*ra_tov)
> +			 * can retry safely
> +			 * even if with same exchange id
> +			 */
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_send_tgt_plogi(iport, tport);
> +		} else {
> +			/* exceeded plogi retry count */
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_send_delete_tport_msg(tport);
> +		}
> +		break;
> +	case FDLS_TGT_STATE_PRLI:
> +		/* PRLI received a LS_RJT with busy , hence we retry from here */
> +		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +			fdls_free_tgt_oxid(iport, oxid);
> +			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_tgt_prli(iport, tport);
> +			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +			return;

And here.

> +		}
> +		/* PRLI has time out 2*ed_tov send abts */
> +		if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +			fdls_send_tport_abts(iport, tport);
> +		} else {
> +			/* ABTS has timed out for prli, we go back to PLOGI */
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_send_tgt_plogi(iport, tport);
> +			fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
> +		}
> +		break;
> +	case FDLS_TGT_STATE_ADISC:
> +		/* ADISC timed out send a ABTS */
> +		if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +			fdls_send_tport_abts(iport, tport);
> +			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +			return;Just call 'break' here instead of spin_unlock/return.

> +		} else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)
> +				   && (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +			/*
> +			 * ABTS has timed out have waited (2*ra_tov) can
> +			 * retry safely even if with same exchange id
> +			 */
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_send_tgt_adisc(iport, tport);
> +
> +			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +			return;
Same here.

> +		}
> +		/* 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);

just call 'break' here.

> +		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		return;
> +	default:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Unknown tport state: 0x%x", tport->state);
> +		break;
> +	}
> +	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +}
> +
> +static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
> +{
> +	iport->fabric.retry_counter = 0;
> +	fdls_send_fabric_flogi(iport);
> +	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
> +	iport->fabric.flags = 0;
> +}
> +
> +static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
> +{
> +	iport->fabric.retry_counter = 0;
> +	fdls_send_fabric_plogi(iport);
> +	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
> +	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
> +}
> +
> +static void
> +fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
> +						   struct fc_hdr_s *fchdr)
> +{
> +	uint32_t tgt_fcid;
> +	struct fnic_tport_s *tport;
> +	uint8_t *fcid;
> +	uint64_t frame_wwnn;
> +	uint64_t frame_wwpn;
> +	uint16_t oxid;
> +	struct fc_els_adisc_ls_acc_s *adisc_rsp =
> +		(struct fc_els_adisc_ls_acc_s *) fchdr;
> +	struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
> +	struct fnic *fnic = iport->fnic;
> +
> +	fcid = FNIC_GET_S_ID(fchdr);
> +	tgt_fcid = ntoh24(fcid);
> +	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
> +
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Tgt ADISC response tport not found: 0x%x", tgt_fcid);
> +		return;
> +	}
> +	if ((iport->state != FNIC_IPORT_STATE_READY)
> +		|| (tport->state != FDLS_TGT_STATE_ADISC)
> +		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Dropping this ADISC response");
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "iport state: %d tport state: %d Is abort issued on PRLI? %d",
> +			 iport->state, tport->state,
> +			 (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED));
> +		return;
> +	}
> +	if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Dropping frame from target: 0x%x",
> +			 tgt_fcid);
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery");
> +		return;
> +	}
> +
> +	switch (adisc_rsp->command) {
> +	case FC_LS_ACC:
> +		if (tport->timer_pending) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "tport 0x%p Canceling fabric disc timer\n",
> +						 tport);
> +			fnic_del_tport_timer_sync();
> +		}
> +		tport->timer_pending = 0;
> +		tport->retry_counter = 0;
> +		oxid = ntohs(fchdr->ox_id);
> +		fdls_free_tgt_oxid(iport, oxid);
> +		frame_wwnn = htonll(adisc_rsp->node_name);
> +		frame_wwpn = htonll(adisc_rsp->nport_name);
> +		if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "ADISC accepted from target: 0x%x. Target logged in",
> +				 tgt_fcid);
> +			fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "Error mismatch frame: ADISC");
> +		}
> +		break;
> +
> +	case FC_LS_REJ:
> +		if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
> +			 || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
> +			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "ADISC ret FC_LS_REJ BUSY. Retry from timer routine: 0x%x",
> +				 tgt_fcid);
> +
> +			/* Retry ADISC again from the timer routine. */
> +			tport->flags |= FNIC_FDLS_RETRY_FRAME;
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "ADISC returned FC_LS_REJ from target: 0x%x",
> +						 tgt_fcid);
> +			oxid = ntohs(fchdr->ox_id);
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_delete_tport(iport, tport);
> +		}
> +		break;
> +	}
> +}
> +
> +
> +static void
> +fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
> +						   struct fc_hdr_s *fchdr)
> +{
> +	uint32_t tgt_fcid;
> +	struct fnic_tport_s *tport;
> +	uint8_t *fcid;
> +	uint16_t oxid;
> +	struct fc_els_s *plogi_rsp = (struct fc_els_s *) fchdr;
> +	struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
> +	int max_payload_size;
> +	struct fnic *fnic = iport->fnic;
> +
> +	fcid = FNIC_GET_S_ID(fchdr);
> +	tgt_fcid = ntoh24(fcid);
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS processing target PLOGI response: tgt_fcid: 0x%x",
> +				 tgt_fcid);
> +
> +	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport not found: 0x%x", tgt_fcid);
> +		return;
> +	}
> +	if ((iport->state != FNIC_IPORT_STATE_READY)
> +		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Dropping frame! iport state: %d tport state: %d",
> +					 iport->state, tport->state);
> +		return;
> +	}
> +
> +	if (tport->state != FDLS_TGT_STATE_PLOGI) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "PLOGI rsp recvd in wrong state. Restarting nexus");
> +		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
> +		fdls_free_tgt_oxid(iport, oxid);
> +		fdls_target_restart_nexus(tport);
> +		return;
> +	}
> +
> +	if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "PLOGI response from target: 0x%x. Dropping frame",
> +			 tgt_fcid);
> +		return;
> +	}
> +
> +	switch (plogi_rsp->command) {
> +	case FC_LS_ACC:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PLOGI accepted by target: 0x%x", tgt_fcid);
> +		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
> +		fdls_free_tgt_oxid(iport, oxid);
> +		break;
> +
> +	case FC_LS_REJ:
> +		if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
> +			 || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
> +			&& (tport->retry_counter < iport->max_plogi_retries)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "PLOGI ret FC_LS_REJ BUSY. Retry from timer routine: 0x%x",
> +				 tgt_fcid);
> +			/* Retry plogi again from the timer routine. */
> +			tport->flags |= FNIC_FDLS_RETRY_FRAME;
> +			return;
> +		}
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PLOGI returned FC_LS_REJ from target: 0x%x",
> +					 tgt_fcid);
> +		oxid = ntohs(fchdr->ox_id);
> +		fdls_free_tgt_oxid(iport, oxid);
> +		fdls_delete_tport(iport, tport);
> +		return;
> +
> +	default:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PLOGI not accepted from target fcid: 0x%x",
> +					 tgt_fcid);
> +		return;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Found the PLOGI target: 0x%x and state: %d",
> +				 (unsigned int) tgt_fcid, tport->state);
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync();
> +	}
> +
> +	tport->timer_pending = 0;
> +	tport->wwpn = htonll(plogi_rsp->nport_name);
> +	tport->wwnn = htonll(plogi_rsp->node_name);
> +
> +	/* Learn the Service Params */
> +
> +	/* Max frame size - choose the lowest */
> +	max_payload_size = fnic_fc_plogi_rsp_rdf(iport, plogi_rsp);
> +	tport->max_payload_size =
> +		MIN(max_payload_size, iport->max_payload_size);
> +
> +	if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "MFS: tport max frame size below spec bounds: %d",
> +			 tport->max_payload_size);
> +		tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +		 "MAX frame size: %d iport max_payload_size: %d tport mfs: %d",
> +		 max_payload_size, iport->max_payload_size,
> +		 tport->max_payload_size);
> +
> +	tport->max_concur_seqs = FNIC_FC_PLOGI_RSP_CONCUR_SEQ(plogi_rsp);
> +
> +	tport->retry_counter = 0;
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_PRLI);
> +	fdls_send_tgt_prli(iport, tport);
> +}
> +
> +static void
> +fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
> +						  struct fc_hdr_s *fchdr)
> +{
> +	uint32_t tgt_fcid;
> +	struct fnic_tport_s *tport;
> +	uint8_t *fcid;
> +	uint16_t oxid;
> +	struct fc_els_prli_s *prli_rsp = (struct fc_els_prli_s *) fchdr;
> +	struct fc_els_reject_s *els_rjt = (struct fc_els_reject_s *) fchdr;
> +	struct fnic_tport_event_s *tport_add_evt;
> +	struct fnic *fnic = iport->fnic;
> +	bool mismatched_tgt = false;
> +
> +	fcid = FNIC_GET_S_ID(fchdr);
> +	tgt_fcid = ntoh24(fcid);
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS process tgt PRLI response: 0x%x", tgt_fcid);
> +
> +	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport not found: 0x%x", tgt_fcid);
> +		/* Handle or just drop? */
> +		return;
> +	}
> +
> +	if ((iport->state != FNIC_IPORT_STATE_READY)
> +		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x",
> +			 iport->state, tport->state, tport->fcid);
> +		return;
> +	}
> +
> +	if (tport->state != FDLS_TGT_STATE_PRLI) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PRLI rsp recvd in wrong state. Restarting nexus");
> +		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
> +		fdls_free_tgt_oxid(iport, oxid);
> +		fdls_target_restart_nexus(tport);
> +		return;
> +	}
> +
> +	if (ntohs(fchdr->ox_id) != ntohs(tport->oxid_used)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Dropping PRLI response from target: 0x%x ",
> +			 tgt_fcid);
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery");
> +		return;
> +	}
> +
> +	switch (prli_rsp->command) {
> +	case FC_LS_ACC:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PRLI accepted from target: 0x%x", tgt_fcid);
> +		oxid = ntohs(FNIC_GET_OX_ID(fchdr));
> +		fdls_free_tgt_oxid(iport, oxid);
> +
> +		if (prli_rsp->sp.type != FC_FC4_TYPE_SCSI) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "mismatched target zoned with FC SCSI initiator: 0x%x",
> +				 tgt_fcid);
> +			mismatched_tgt = true;
>   		}
> -		break;
> -	case FDLS_STATE_REGISTER_FC4_TYPES:
> -		/* scr received a LS_RJT with busy we retry from here */
> -		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> -			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> -			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> -			fdls_send_register_fc4_types(iport);
> -			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		if (mismatched_tgt) {
> +			fdls_tgt_logout(iport, tport);
> +			fdls_delete_tport(iport, tport);
>   			return;
>   		}
> -		/* RFT_ID timed out send abts */
> -		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
> -			fdls_send_fabric_abts(iport);
> -		} else {
> -			/* ABTS has timed out (2*ra_tov), we give up */
> -			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -						 "ABTS timed out. Starting PLOGI: %p", iport);
> -			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
> -		}
>   		break;
> -	case FDLS_STATE_REGISTER_FC4_FEATURES:
> -		/* scr received a LS_RJT with busy we retry from here */
> -		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> -			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> -			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> -			fdls_send_register_fc4_features(iport);
> -			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> -			return;
> -		}
> -		/* scr have timed out send abts */
> -		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
> -			fdls_send_fabric_abts(iport);
> -		else {
> -			/* ABTS has timed out (2*ra_tov), we give up */
> +
> +	case FC_LS_REJ:
> +		if (((els_rjt->reason_code == FC_ELS_RJT_LOGICAL_BUSY)
> +			 || (els_rjt->reason_code == FC_ELS_RJT_BUSY))
> +			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +
>   			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -						 "ABTS timed out. Starting PLOGI %p", iport);
> -			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
> -		}
> -		break;
> -	case FDLS_STATE_RSCN_GPN_FT:
> -	case FDLS_STATE_SEND_GPNFT:
> -	case FDLS_STATE_GPN_FT:
> -		/* GPN_FT received a LS_RJT with busy we retry from here */
> -		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> -			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> -			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> -			fdls_send_gpn_ft(iport, iport->fabric.state);
> -			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +				 "PRLI ret FC_LS_REJ BUSY. Retry from timer routine: 0x%x",
> +				 tgt_fcid);
> +
> +			/*Retry Plogi again from the timer routine. */
> +			tport->flags |= FNIC_FDLS_RETRY_FRAME;
>   			return;
> -		}
> -		/* gpn_gt have timed out send abts */
> -		if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
> -			fdls_send_fabric_abts(iport);
>   		} else {
> -			/*
> -			 * ABTS has timed out have waited (2*ra_tov) can
> -			 * retry safely with same exchange id
> -			 */
> -			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
> -				fdls_send_gpn_ft(iport, iport->fabric.state);
> -			} else {
> -				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
> -					 iport);
> -			}
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "PRLI returned FC_LS_REJ from target: 0x%x",
> +						 tgt_fcid);
> +
> +			oxid = ntohs(fchdr->ox_id);
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_tgt_logout(iport, tport);
> +			fdls_delete_tport(iport, tport);
> +			return;
>   		}
>   		break;
> +
>   	default:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PRLI not accepted from target: 0x%x", tgt_fcid);
> +		return;
>   		break;
>   	}
> -	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> -}
>   
That has quite an odd oxid handling. For some places the oxid is 
released, on others it isn't.
With no clear indication when.
If you want to have the oxid reused please add some comments to
make it clear that this isn't an oversight.

> -static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
> -{
> -	iport->fabric.retry_counter = 0;
> -	fdls_send_fabric_flogi(iport);
> -	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
> -	iport->fabric.flags = 0;
> -}
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Found the PRLI target: 0x%x and state: %d",
> +				 (unsigned int) tgt_fcid, tport->state);
>   
> -static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
> -{
> -	iport->fabric.retry_counter = 0;
> -	fdls_send_fabric_plogi(iport);
> -	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
> -	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync();
> +	}
> +	tport->timer_pending = 0;
> +
> +	oxid = ntohs(FNIC_GET_OX_ID(fchdr));
> +	fdls_free_tgt_oxid(iport, oxid);
> +
> +	/* Learn Service Params */
> +	tport->fcp_csp = ntohl(prli_rsp->sp.csp);
> +	tport->retry_counter = 0;
> +
> +	if (prli_rsp->sp.csp & FCP_SPPF_RETRY)
> +		tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
> +
> +	/* Check if the device plays Target Mode Function */
> +	if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Remote port(0x%x): no target support. Deleting it\n",
> +			 tgt_fcid);
> +		fdls_tgt_logout(iport, tport);
> +		fdls_delete_tport(iport, tport);
> +		return;
> +	}
> +
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
> +
> +	/* Inform the driver about new target added */
> +	tport_add_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
> +	if (!tport_add_evt) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "tport event memory allocation failure: 0x%0x\n",
> +				 tport->fcid);
> +		return;
> +	}
> +	tport_add_evt->event = TGT_EV_RPORT_ADD;
> +	tport_add_evt->arg1 = (void *) tport;
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "iport fcid: 0x%x add tport event fcid: 0x%x\n",
> +			 tport->fcid, iport->fcid);
> +	list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
> +	queue_work(fnic_event_queue, &fnic->tport_work);
>   }
>   
> +
>   static void
>   fdls_process_rff_id_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr)
>   {
> @@ -884,6 +1918,106 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr)
>   	}
>   }
>   
> +static void
> +fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
> +							 struct fc_hdr_s *fchdr, int len)
> +{
> +	struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt;
> +	struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt_rem;
> +	struct fnic_tport_s *tport, *next;
> +	uint32_t fcid;
> +	uint64_t wwpn;
> +	int rem_len = len;
> +	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
> +
> +	gpn_ft_tgt =
> +		(struct fc_gpn_ft_rsp_iu_s *) ((uint8_t *) fchdr +
> +								   sizeof(struct fc_hdr_s)
> +								   + sizeof(struct fc_ct_hdr_s));
> +	gpn_ft_tgt_rem = gpn_ft_tgt;
> +	len -= sizeof(struct fc_hdr_s) + sizeof(struct fc_ct_hdr_s);
> +
> +	while (rem_len > 0) {
> +
> +		fcid = ntoh24(gpn_ft_tgt->fcid);
> +		wwpn = ntohll(gpn_ft_tgt->wwpn);
> +
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
> +
> +		if (fcid == iport->fcid) {
> +			if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
> +				break;
> +			gpn_ft_tgt++;
> +			rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
> +			continue;
> +		}
> +
> +		tport = fnic_find_tport_by_wwpn(iport, wwpn);
> +		if (!tport) {
> +			/*
> +			 * New port registered with the switch or first time query
> +			 */
> +			tport = fdls_create_tport(iport, fcid, wwpn);
> +			if (!tport)
> +				return;
> +		}
> +		/*
> +		 * check if this was an existing tport with same fcid
> +		 * but whose wwpn has changed now ,then remove it and
> +		 * create a new one
> +		 */
> +		if (tport->fcid != fcid) {
> +			fdls_delete_tport(iport, tport);
> +			tport = fdls_create_tport(iport, fcid, wwpn);
> +			if (!tport)
> +				return;
> +		}
> +
> +		/*
> +		 * If this GPN_FT rsp is after RSCN then mark the tports which
> +		 * matches with the new GPN_FT list, if some tport is not
> +		 * found in GPN_FT we went to delete that tport later.
> +		 */
> +		if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT)
> +			tport->flags |= FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
> +
> +		if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
> +			break;
> +
> +		gpn_ft_tgt++;
> +		rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
> +	}
> +	if (rem_len <= 0) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
> +			 len, rem_len);
> +	}
> +
> +	/*remove those ports which was not listed in GPN_FT */
> +	if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT) {
> +		list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
> +
> +			if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Remove port: 0x%x not found in GPN_FT list",
> +					 tport->fcid);
> +				fdls_delete_tport(iport, tport);
> +			} else {
> +				tport->flags &= ~FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
> +			}
> +			if ((old_link_down_cnt != iport->fnic->link_down_cnt)
> +				|| (iport->state != FNIC_IPORT_STATE_READY)) {
> +				return;
> +			}
> +		}
> +	}
> +}
> +
>   static void
>   fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
>   						int len)
> @@ -892,6 +2026,9 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
>   	struct fc_gpn_ft_s *gpn_ft_rsp = (struct fc_gpn_ft_s *) fchdr;
>   	uint16_t rsp;
>   	uint8_t reason_code;
> +	int count = 0;
> +	struct fnic_tport_s *tport, *next;
> +	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
>   	struct fnic *fnic = iport->fnic;
>   
>   	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> @@ -928,12 +2065,74 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fc_hdr_s *fchdr,
>   	case FC_CT_ACC:
>   		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
>   					 "0x%x: GPNFT_RSP accept", iport->fcid);
> +		if (iport->fabric.timer_pending) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "0x%x: Canceling fabric disc timer\n",
> +						 iport->fcid);
> +			fnic_del_fabric_timer_sync();
> +		}
> +		iport->fabric.timer_pending = 0;
> +		iport->fabric.retry_counter = 0;
> +		fdls_process_gpn_ft_tgt_list(iport, fchdr, len);
> +
> +		/*
> +		 * iport state can change only if link down event happened
> +		 * We don't need to undo fdls_process_gpn_ft_tgt_list,
> +		 * that will be taken care in next link up event
> +		 */
> +		if (iport->state != FNIC_IPORT_STATE_READY) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Halting target discovery: fab st: %d iport st: %d ",
> +				 fdls_get_state(fdls), iport->state);
> +			break;
> +		}
> +		fdls_tgt_discovery_start(iport);
>   		break;
>   
>   	case FC_CT_REJ:
>   		reason_code = gpn_ft_rsp->fc_ct_hdr.reason_code;
>   		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
>   					 "0x%x: GPNFT_RSP Reject", iport->fcid);
> +
> +		if (((reason_code == FC_CT_RJT_LOGICAL_BUSY)
> +			 || (reason_code == FC_CT_RJT_BUSY))
> +			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine",
> +				 iport->fcid);
> +			/* Retry again from the timer routine */
> +			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "0x%x: GPNFT_RSP reject", iport->fcid);
> +			if (iport->fabric.timer_pending) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +							 "0x%x: Canceling fabric disc timer\n",
> +							 iport->fcid);
> +				fnic_del_fabric_timer_sync();
> +			}
> +			iport->fabric.timer_pending = 0;
> +			iport->fabric.retry_counter = 0;
> +			/*
> +			 * If GPN_FT ls_rjt then we should delete
> +			 * all existing tports
> +			 */
> +			count = 0;
> +			list_for_each_entry_safe(tport, next, &iport->tport_list,
> +									 links) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +							 "GPN_FT_REJECT: Remove port: 0x%x",
> +							 tport->fcid);
> +				fdls_delete_tport(iport, tport);
> +				if ((old_link_down_cnt != iport->fnic->link_down_cnt)
> +					|| (iport->state != FNIC_IPORT_STATE_READY)) {
> +					return;
> +				}
> +				count++;
> +			}
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "GPN_FT_REJECT: Removed (0x%x) ports", count);
> +		}
>   		break;
>   
>   	default:
> @@ -1342,6 +2541,141 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
>   	}
>   }
>   
> +static void
> +fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
> +						  struct fc_hdr_s *fchdr)
> +{
> +	uint32_t s_id;
> +	struct fnic_tport_s *tport;
> +	uint32_t tport_state;
> +	struct fc_abts_ba_acc_s *ba_acc;
> +	struct fc_abts_ba_rjt_s *ba_rjt;
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +
> +	s_id = ntoh24(fchdr->sid);
> +	ba_acc = (struct fc_abts_ba_acc_s *) fchdr;
> +	ba_rjt = (struct fc_abts_ba_rjt_s *) fchdr;
> +
> +	tport = fnic_find_tport_by_fcid(iport, s_id);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "Received tgt abts rsp with invalid SID: 0x%x", s_id);
> +		return;
> +	}
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "tport 0x%p Canceling fabric disc timer\n", tport);
> +		fnic_del_tport_timer_sync();
> +	}
> +	if (iport->state != FNIC_IPORT_STATE_READY) {
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "Received tgt abts rsp in iport state(%d). Dropping.",
> +					 iport->state);
> +		return;
> +	}
> +	tport->timer_pending = 0;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +	tport_state = tport->state;
> +	oxid = ntohs(fchdr->ox_id);
> +
> +	/*This abort rsp is for ADISC */
> +	if ((oxid >= FDLS_ADISC_OXID_BASE) && (oxid < FDLS_TGT_OXID_POOL_END)) {
> +		if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +						 "Received tgt ADISC abts response BA_ACC for OX_ID: 0x%x tgt_fcid: 0x%x",
> +						 ba_acc->ox_id, tport->fcid);
> +		} else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +				 "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ",
> +				 tport->fcid, tport_state);
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +				 "reason code: 0x%x reason code explanation:0x%x ",
> +				 ba_rjt->reason_code, ba_rjt->reason_explanation);
> +		}
> +		if ((tport->retry_counter < FDLS_RETRY_COUNT)
> +			&& (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_send_tgt_adisc(iport, tport);
> +			return;
> +		}
> +		fdls_free_tgt_oxid(iport, oxid);
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "ADISC not responding. Deleting target port: 0x%x",
> +					 tport->fcid);
> +		fdls_delete_tport(iport, tport);
> +		if ((iport->state == FNIC_IPORT_STATE_READY)
> +			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
> +			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
> +			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
> +		}
> +		/*Restart a discovery of targets */
> +		return;
> +	}
> +
> +	/*This abort rsp is for PLOGI */
> +	if ((oxid >= FDLS_PLOGI_OXID_BASE) && (oxid < FDLS_PRLI_OXID_BASE)) {
> +		if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +				 "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x",
> +				 tport->fcid);
> +		} else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x",
> +				 tport->fcid, fchdr->ox_id);
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "reason code: 0x%x reason code explanation: 0x%x",
> +				  ba_rjt->reason_code, ba_rjt->reason_explanation);
> +		}
> +		if ((tport->retry_counter < iport->max_plogi_retries)
> +			&& (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_send_tgt_plogi(iport, tport);
> +			return;
> +		}
> +		fdls_free_tgt_oxid(iport, oxid);
> +		fdls_delete_tport(iport, tport);
> +		/*Restart a discovery of targets */
> +		if ((iport->state == FNIC_IPORT_STATE_READY)
> +			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
> +			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
> +			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
> +		}
> +		return;
> +	}
> +
> +	/*This abort rsp is for PRLI */
> +	if ((oxid >= FDLS_PRLI_OXID_BASE) && (oxid < FDLS_ADISC_OXID_BASE)) {
> +		if (fchdr->r_ctl == FNIC_BA_ACC_RCTL) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: Received tgt PRLI abts response BA_ACC",
> +				 tport->fcid);
> +		} else if (fchdr->r_ctl == FNIC_BA_RJT_RCTL) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ",
> +				 tport->fcid, fchdr->ox_id);
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "reason code: 0x%x reason code explanation: 0x%x",
> +				 ba_rjt->reason_code,
> +				 ba_rjt->reason_explanation);
> +		}
> +		if ((tport->retry_counter < FDLS_RETRY_COUNT)
> +			&& (fchdr->r_ctl == FNIC_BA_ACC_RCTL)) {
> +			fdls_free_tgt_oxid(iport, oxid);
> +			fdls_send_tgt_prli(iport, tport);
> +			return;
> +		}
> +		fdls_free_tgt_oxid(iport, oxid);
> +		fdls_send_tgt_plogi(iport, tport);	/* go back to plogi */
> +		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
> +		return;
> +	}
> +
I did comment on dedicated OXIDs in the previous patch. Please, don't. 
Use a common pool for oxids.

> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Received ABTS response for unknown frame %p", iport);
> +}
> +
>   /*
>    * Performs a validation for all FCOE frames and return the frame type
>    */
> @@ -1434,6 +2768,42 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
>   		}
>   	}
>   
> +	/* ELS response from a target */
> +	if ((ntohs(oxid) >= FDLS_PLOGI_OXID_BASE)
> +		&& (ntohs(oxid) < FDLS_PRLI_OXID_BASE)) {
> +		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"Dropping Unknown frame in PLOGI exchange range type: 0x%x.",
> +				fchdr->type);
> +			return -1;
> +		}
> +		return FNIC_TPORT_PLOGI_RSP;
> +	}
> +	if ((ntohs(oxid) >= FDLS_PRLI_OXID_BASE)
> +		&& (ntohs(oxid) < FDLS_ADISC_OXID_BASE)) {
> +		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"Dropping Unknown frame in PRLI exchange range type: 0x%x.",
> +				fchdr->type);
> +			return -1;
> +		}
> +		return FNIC_TPORT_PRLI_RSP;
> +	}
> +
> +	if ((ntohs(oxid) >= FDLS_ADISC_OXID_BASE)
> +		&& (ntohs(oxid) < FDLS_TGT_OXID_POOL_END)) {
> +		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"Dropping Unknown frame in ADISC exchange range type: 0x%x.",
> +				fchdr->type);
> +			return -1;
> +		}
> +		return FNIC_TPORT_ADISC_RSP;
> +	}
> +	if (ntohs(oxid) == FNIC_TLOGO_REQ_OXID) {
> +		return FNIC_TPORT_LOGO_RSP;
> +	}
> +
>   	/*response from fabric */
>   	switch (oxid) {
>   
> @@ -1557,6 +2927,21 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
>   	case FNIC_FABRIC_GPN_FT_RSP:
>   		fdls_process_gpn_ft_rsp(iport, fchdr, len);
>   		break;
> +	case FNIC_TPORT_PLOGI_RSP:
> +		fdls_process_tgt_plogi_rsp(iport, fchdr);
> +		break;
> +	case FNIC_TPORT_PRLI_RSP:
> +		fdls_process_tgt_prli_rsp(iport, fchdr);
> +		break;
> +	case FNIC_TPORT_ADISC_RSP:
> +		fdls_process_tgt_adisc_rsp(iport, fchdr);
> +		break;
> +	case FNIC_TPORT_LOGO_RSP:
> +		/* Logo response from tgt which we have deleted */
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Logo response from tgt: 0x%x",
> +					 ntoh24(fchdr->sid));
> +		break;
>   	case FNIC_FABRIC_LOGO_RSP:
>   		fdls_process_fabric_logo_rsp(iport, fchdr);
>   		break;
> @@ -1566,7 +2951,8 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
>   		if ((iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)
>   			&& (oxid >= FNIC_FLOGI_OXID && oxid <= FNIC_RFF_REQ_OXID)) {
>   			fdls_process_fabric_abts_rsp(iport, fchdr);
> -		}
> +		} else
> +			fdls_process_tgt_abts_rsp(iport, fchdr);
>   		break;
>   	default:
>   		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
> index 2d5f438f2cc4..92cd17efa40f 100644
> --- a/drivers/scsi/fnic/fnic.h
> +++ b/drivers/scsi/fnic/fnic.h
> @@ -79,6 +79,9 @@
>   
>   #define IS_FNIC_FCP_INITIATOR(fnic) (fnic->role == FNIC_ROLE_FCP_INITIATOR)
>   
> +/* Retry supported by rport (returned by PRLI service parameters) */
> +#define FNIC_FC_RP_FLAGS_RETRY            0x1
> +
>   /*
>    * fnic private data per SCSI command.
>    * These fields are locked by the hashed io_req_lock.
> @@ -133,6 +136,7 @@ static inline u64 fnic_flags_and_state(struct scsi_cmnd *cmd)
>   
>   extern unsigned int fnic_log_level;
>   extern unsigned int io_completions;
> +extern struct workqueue_struct *fnic_event_queue;
>   
>   #define FNIC_MAIN_LOGGING 0x01
>   #define FNIC_FCS_LOGGING 0x02
> @@ -329,6 +333,8 @@ struct fnic {
>   	struct work_struct flush_work;
>   	struct sk_buff_head frame_queue;
>   	struct list_head tx_queue;
> +	struct work_struct tport_work;
> +	struct list_head tport_event_list;
>   
>   	/*** FIP related data members  -- start ***/
>   	void (*set_vlan)(struct fnic *, u16 vlan);
> diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
> index 095275698716..9eb25ed9c19f 100644
> --- a/drivers/scsi/fnic/fnic_fdls.h
> +++ b/drivers/scsi/fnic/fnic_fdls.h
> @@ -325,6 +325,10 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport);
>   int fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
>   								  void *rx_frame, int len,
>   								  int fchdr_offset);
> +void fdls_send_tport_abts(struct fnic_iport_s *iport,
> +						  struct fnic_tport_s *tport);
> +void fdls_delete_tport(struct fnic_iport_s *iport,
> +					   struct fnic_tport_s *tport);
>   
>   /* fnic_fcs.c */
>   void fnic_fdls_init(struct fnic *fnic, int usefip);

Cheers,

Hannes
Karan Tilak Kumar (kartilak) June 11, 2024, 5:48 p.m. UTC | #2
On Tuesday, June 11, 2024 6:32 AM, Hannes Reinecke <hare@suse.de> wrote:
> Better use a bitmap here; fdls_alloc_tgt_oxid() is not atomic, and will
> need to be locked to avoid races.

Thanks for your review comments so far Hannes. Appreciate you taking the time.
I'll address them in the next version.

Regards,
Karan
kernel test robot June 11, 2024, 5:56 p.m. UTC | #3
Hi Karan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on mkp-scsi/for-next]
[also build test WARNING on jejb-scsi/for-next linus/master v6.10-rc3 next-20240611]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Karan-Tilak-Kumar/scsi-fnic-Replace-shost_printk-with-pr_info-pr_err/20240611-060227
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git for-next
patch link:    https://lore.kernel.org/r/20240610215100.673158-5-kartilak%40cisco.com
patch subject: [PATCH 04/14] scsi: fnic: Add support for target based solicited requests and responses
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20240612/202406120146.xchlZbqX-lkp@intel.com/config)
compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240612/202406120146.xchlZbqX-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406120146.xchlZbqX-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/scsi/fnic/fdls_disc.c:1926:29: warning: variable 'gpn_ft_tgt_rem' set but not used [-Wunused-but-set-variable]
    1926 |         struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt_rem;
         |                                    ^
   drivers/scsi/fnic/fdls_disc.c:2208:26: warning: variable 'els_rjt' set but not used [-Wunused-but-set-variable]
    2208 |         struct fc_els_reject_s *els_rjt;
         |                                 ^
   drivers/scsi/fnic/fdls_disc.c:2890:11: warning: variable 's_id' set but not used [-Wunused-but-set-variable]
    2890 |         uint32_t s_id = 0;
         |                  ^
   drivers/scsi/fnic/fdls_disc.c:2891:11: warning: variable 'd_id' set but not used [-Wunused-but-set-variable]
    2891 |         uint32_t d_id = 0;
         |                  ^
   drivers/scsi/fnic/fdls_disc.c:1286:13: warning: unused function 'fnic_fdls_start_flogi' [-Wunused-function]
    1286 | static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
         |             ^~~~~~~~~~~~~~~~~~~~~
   5 warnings generated.


vim +/gpn_ft_tgt_rem +1926 drivers/scsi/fnic/fdls_disc.c

  1920	
  1921	static void
  1922	fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
  1923								 struct fc_hdr_s *fchdr, int len)
  1924	{
  1925		struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt;
> 1926		struct fc_gpn_ft_rsp_iu_s *gpn_ft_tgt_rem;
  1927		struct fnic_tport_s *tport, *next;
  1928		uint32_t fcid;
  1929		uint64_t wwpn;
  1930		int rem_len = len;
  1931		u32 old_link_down_cnt = iport->fnic->link_down_cnt;
  1932		struct fnic *fnic = iport->fnic;
  1933	
  1934		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
  1935					 "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
  1936	
  1937		gpn_ft_tgt =
  1938			(struct fc_gpn_ft_rsp_iu_s *) ((uint8_t *) fchdr +
  1939									   sizeof(struct fc_hdr_s)
  1940									   + sizeof(struct fc_ct_hdr_s));
  1941		gpn_ft_tgt_rem = gpn_ft_tgt;
  1942		len -= sizeof(struct fc_hdr_s) + sizeof(struct fc_ct_hdr_s);
  1943	
  1944		while (rem_len > 0) {
  1945	
  1946			fcid = ntoh24(gpn_ft_tgt->fcid);
  1947			wwpn = ntohll(gpn_ft_tgt->wwpn);
  1948	
  1949			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
  1950					 "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
  1951	
  1952			if (fcid == iport->fcid) {
  1953				if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
  1954					break;
  1955				gpn_ft_tgt++;
  1956				rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
  1957				continue;
  1958			}
  1959	
  1960			tport = fnic_find_tport_by_wwpn(iport, wwpn);
  1961			if (!tport) {
  1962				/*
  1963				 * New port registered with the switch or first time query
  1964				 */
  1965				tport = fdls_create_tport(iport, fcid, wwpn);
  1966				if (!tport)
  1967					return;
  1968			}
  1969			/*
  1970			 * check if this was an existing tport with same fcid
  1971			 * but whose wwpn has changed now ,then remove it and
  1972			 * create a new one
  1973			 */
  1974			if (tport->fcid != fcid) {
  1975				fdls_delete_tport(iport, tport);
  1976				tport = fdls_create_tport(iport, fcid, wwpn);
  1977				if (!tport)
  1978					return;
  1979			}
  1980	
  1981			/*
  1982			 * If this GPN_FT rsp is after RSCN then mark the tports which
  1983			 * matches with the new GPN_FT list, if some tport is not
  1984			 * found in GPN_FT we went to delete that tport later.
  1985			 */
  1986			if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT)
  1987				tport->flags |= FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
  1988	
  1989			if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
  1990				break;
  1991	
  1992			gpn_ft_tgt++;
  1993			rem_len -= sizeof(struct fc_gpn_ft_rsp_iu_s);
  1994		}
  1995		if (rem_len <= 0) {
  1996			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
  1997				 "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
  1998				 len, rem_len);
  1999		}
  2000	
  2001		/*remove those ports which was not listed in GPN_FT */
  2002		if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT) {
  2003			list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
  2004	
  2005				if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
  2006					FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
  2007						 "Remove port: 0x%x not found in GPN_FT list",
  2008						 tport->fcid);
  2009					fdls_delete_tport(iport, tport);
  2010				} else {
  2011					tport->flags &= ~FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
  2012				}
  2013				if ((old_link_down_cnt != iport->fnic->link_down_cnt)
  2014					|| (iport->state != FNIC_IPORT_STATE_READY)) {
  2015					return;
  2016				}
  2017			}
  2018		}
  2019	}
  2020
diff mbox series

Patch

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