diff mbox series

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

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

Commit Message

Karan Tilak Kumar (kartilak) Oct. 18, 2024, 4:13 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.

Reported-by: kernel test robot <lkp@intel.com>
Closes:
https://lore.kernel.org/oe-kbuild-all/202406120146.xchlZbqX-lkp@
intel.com/

Reviewed-by: Sesidhar Baddela <sebaddel@cisco.com>
Co-developed-by: Gian Carlo Boffa <gcboffa@cisco.com>
Signed-off-by: Gian Carlo Boffa <gcboffa@cisco.com>
Co-developed-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Signed-off-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Co-developed-by: Arun Easi <aeasi@cisco.com>
Signed-off-by: Arun Easi <aeasi@cisco.com>
Co-developed-by: Karan Tilak Kumar <kartilak@cisco.com>
Signed-off-by: Karan Tilak Kumar <kartilak@cisco.com>
---
Changes between v4 and v5:
    Incorporate review comments from Martin:
	Call fdls_get_tgt_oxid_pool.
	Modify attribution appropriately.

Changes between v2 and v3:
    Fix issue found by kernel test robot.
    Remove fnic_std_ba_acc definition to fix compilation
    warning.
    Incorporate review comments from Hannes:
	Replace redundant definitions with standard definitions.
	Replace static OXIDs with pool-based OXIDs for targets.

Changes between v1 and v2:
    Incorporate review comments from Hannes:
	Use the correct kernel-doc format.
	Replace htonll() with get_unaligned_be64().
	Replace fnic_del_fabric_timer_sync macro calls to function
	calls.
	Replace fnic_del_tport_timer_sync macro calls to function
	calls.
	Rename fc_abts_s to fc_tport_abts_s.
	Modify fc_tport_abts_s to be a global frame.
	Rename variable pfc_abts to tport_abts.
	Replace definitions with standard definitions from
	fc_els.h.
	Modify functions with returns in the middle to if else
	clauses.
    Replace simultaneous use of fc_tport_abts_s and tport_abts with
    just tport_abts.
---
 drivers/scsi/fnic/fdls_disc.c | 1569 +++++++++++++++++++++++++++++++--
 drivers/scsi/fnic/fnic.h      |    6 +
 drivers/scsi/fnic/fnic_fdls.h |    2 +-
 3 files changed, 1502 insertions(+), 75 deletions(-)

Comments

Hannes Reinecke Oct. 24, 2024, 7:28 a.m. UTC | #1
On 10/18/24 18:13, Karan Tilak Kumar wrote:
> Add support for target based solicited requests and responses.
> Add support for tport definitions and processing.
> Add support for restarting the IT nexus.
> 
> Reported-by: kernel test robot <lkp@intel.com>
> Closes:
> https://lore.kernel.org/oe-kbuild-all/202406120146.xchlZbqX-lkp@
> intel.com/
> 
> Reviewed-by: Sesidhar Baddela <sebaddel@cisco.com>
> Co-developed-by: Gian Carlo Boffa <gcboffa@cisco.com>
> Signed-off-by: Gian Carlo Boffa <gcboffa@cisco.com>
> Co-developed-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
> Signed-off-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
> Co-developed-by: Arun Easi <aeasi@cisco.com>
> Signed-off-by: Arun Easi <aeasi@cisco.com>
> Co-developed-by: Karan Tilak Kumar <kartilak@cisco.com>
> Signed-off-by: Karan Tilak Kumar <kartilak@cisco.com>
> ---
> Changes between v4 and v5:
>      Incorporate review comments from Martin:
> 	Call fdls_get_tgt_oxid_pool.
> 	Modify attribution appropriately.
> 
> Changes between v2 and v3:
>      Fix issue found by kernel test robot.
>      Remove fnic_std_ba_acc definition to fix compilation
>      warning.
>      Incorporate review comments from Hannes:
> 	Replace redundant definitions with standard definitions.
> 	Replace static OXIDs with pool-based OXIDs for targets.
> 
> Changes between v1 and v2:
>      Incorporate review comments from Hannes:
> 	Use the correct kernel-doc format.
> 	Replace htonll() with get_unaligned_be64().
> 	Replace fnic_del_fabric_timer_sync macro calls to function
> 	calls.
> 	Replace fnic_del_tport_timer_sync macro calls to function
> 	calls.
> 	Rename fc_abts_s to fc_tport_abts_s.
> 	Modify fc_tport_abts_s to be a global frame.
> 	Rename variable pfc_abts to tport_abts.
> 	Replace definitions with standard definitions from
> 	fc_els.h.
> 	Modify functions with returns in the middle to if else
> 	clauses.
>      Replace simultaneous use of fc_tport_abts_s and tport_abts with
>      just tport_abts.
> ---
>   drivers/scsi/fnic/fdls_disc.c | 1569 +++++++++++++++++++++++++++++++--
>   drivers/scsi/fnic/fnic.h      |    6 +
>   drivers/scsi/fnic/fnic_fdls.h |    2 +-
>   3 files changed, 1502 insertions(+), 75 deletions(-)
> 
> diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
> index 3a995b76a864..52459f6bb589 100644
> --- a/drivers/scsi/fnic/fdls_disc.c
> +++ b/drivers/scsi/fnic/fdls_disc.c
> @@ -11,6 +11,10 @@
>   #include <scsi/fc/fc_fcp.h>
>   #include <linux/utsname.h>
>   
> +#define FC_FC4_TYPE_SCSI 0x08
> +
> +static void fdls_send_rpn_id(struct fnic_iport_s *iport);
> +
>   /* Frame initialization */
>   /*
>    * Variables:
> @@ -66,6 +70,20 @@ struct fc_std_rpn_id fnic_std_rpn_id_req = {
>   		      .ct_cmd = cpu_to_be16(FC_NS_RPN_ID)}
>   };
>   
> +/*
> + * Variables:
> + * did, sid, oxid
> + */
> +struct fc_std_els_prli fnic_std_prli_req = {
> +	.fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
> +		  .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}, .fh_rx_id = 0xFFFF},
> +	.els_prli = {.prli_cmd = ELS_PRLI,
> +		     .prli_spp_len = 16,
> +		     .prli_len = cpu_to_be16(0x14)},
> +	.sp = {.spp_type = 0x08, .spp_flags = 0x0020,
> +	       .spp_params = cpu_to_be32(0xA2)}
> +};
> +
>   /*
>    * Variables:
>    * fh_s_id, port_id, port_name
> @@ -143,9 +161,19 @@ struct fc_frame_header fc_std_fabric_abts = {
>   	.fh_parm_offset = 0x00000000,	/* bit:0 = 0 Abort a exchange */
>   };
>   
> +struct fc_frame_header fc_std_tport_abts = {
> +	.fh_r_ctl = FC_RCTL_BA_ABTS,	/* ABTS */
> +	.fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS,
> +	.fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00,
> +	.fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000, .fh_rx_id = 0xFFFF,
> +	.fh_parm_offset = 0x00000000,	/* bit:0 = 0 Abort a exchange */
> +};
> +
>   #define RETRIES_EXHAUSTED(iport)      \
>   	(iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)
>   
> +#define FNIC_TPORT_MAX_NEXUS_RESTART (8)
> +
>   /*
>    * For fabric requests and fdmi, once OXIDs are allocated from the pool
>    * (and a range) they are encoded with expected rsp type as
> @@ -167,6 +195,14 @@ static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
>   				   void *rx_frame);
>   static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
>   static void fnic_fdls_start_flogi(struct fnic_iport_s *iport);
> +static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
> +									  uint32_t fcid,
> +									  uint64_t wwpn);
> +static void fdls_target_restart_nexus(struct fnic_tport_s *tport);
> +static void fdls_start_tport_timer(struct fnic_iport_s *iport,
> +					struct fnic_tport_s *tport, int timeout);
> +static void fdls_tport_timer_callback(struct timer_list *t);
> +
>   static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
>   			int timeout);
>   static void
> @@ -182,6 +218,8 @@ void fdls_init_oxid_pool(struct fnic_iport_s *iport)
>   	fdls_init_fabric_oxid_pool(&iport->fdmi_oxid_pool,
>   			FDLS_FDMI_OXID_POOL_BASE,
>   			FDLS_FDMI_OXID_POOL_SZ);
> +
> +	fdls_init_tgt_oxid_pool(iport);
>   }
>   
>   uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport,
> @@ -313,6 +351,13 @@ static inline void fdls_schedule_fabric_oxid_free(struct fnic_iport_s
>   			    iport->fabric_oxid_pool.active_oxid_fabric_req);
>   }
>   
> +static inline void fdls_schedule_tgt_oxid_free(struct fnic_iport_s *iport,
> +					       struct fnic_tgt_oxid_pool_s
> +					       *oxid_pool, uint16_t oxid)
> +{
> +	fdls_schedule_oxid_free(&oxid_pool->meta, oxid);
> +}
> +
>   int fnic_fdls_expected_rsp(struct fnic_iport_s *iport, uint16_t oxid)
>   {
>   	struct fnic *fnic = iport->fnic;
> @@ -340,6 +385,62 @@ static int fdls_is_oxid_in_fabric_range(uint16_t oxid)
>   			(oxid_unmasked <= FDLS_FABRIC_OXID_POOL_END));
>   }
>   
> +void fdls_init_tgt_oxid_pool(struct fnic_iport_s *iport)
> +{
> +	memset(&iport->plogi_oxid_pool, 0, sizeof(iport->plogi_oxid_pool));
> +	iport->plogi_oxid_pool.meta.oxid_base = FDLS_PLOGI_OXID_BASE;
> +	iport->plogi_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
> +	INIT_LIST_HEAD(&iport->plogi_oxid_pool.meta.reclaim_list);
> +
> +	memset(&iport->prli_oxid_pool, 0, sizeof(iport->prli_oxid_pool));
> +	iport->prli_oxid_pool.meta.oxid_base = FDLS_PRLI_OXID_BASE;
> +	iport->prli_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
> +	INIT_LIST_HEAD(&iport->prli_oxid_pool.meta.reclaim_list);
> +
> +	memset(&iport->adisc_oxid_pool, 0, sizeof(iport->adisc_oxid_pool));
> +	iport->adisc_oxid_pool.meta.oxid_base = FDLS_ADISC_OXID_BASE;
> +	iport->adisc_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
> +	INIT_LIST_HEAD(&iport->adisc_oxid_pool.meta.reclaim_list);
> +}
> +
> +inline uint16_t fdls_alloc_tgt_oxid(struct fnic_iport_s *iport,
> +				    struct fnic_tgt_oxid_pool_s *oxid_pool)
> +{
> +	uint16_t oxid;
> +
> +	oxid = fdls_alloc_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap);
> +	return oxid;
> +}

Do a 'return fdls_alloc_oxid()' and drop the intermediate variable.

> +
> +inline void fdls_free_tgt_oxid(struct fnic_iport_s *iport,
> +			       struct fnic_tgt_oxid_pool_s *oxid_pool,
> +			       uint16_t oxid)
> +{
> +	fdls_free_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap, oxid);
> +}
> +
> +static struct fnic_tgt_oxid_pool_s *fdls_get_tgt_oxid_pool(struct fnic_tport_s
> +							   *tport)
> +{
> +	struct fnic_iport_s *iport = (struct fnic_iport_s *)tport->iport;
> +	struct fnic_tgt_oxid_pool_s *oxid_pool = NULL;
> +
> +	switch (tport->state) {
> +	case FDLS_TGT_STATE_PLOGI:
> +		oxid_pool = &iport->plogi_oxid_pool;
> +		break;
> +	case FDLS_TGT_STATE_PRLI:
> +		oxid_pool = &iport->prli_oxid_pool;
> +		break;
> +	case FDLS_TGT_STATE_ADISC:
> +		oxid_pool = &iport->adisc_oxid_pool;
> +		break;
> +	default:
> +		break;
> +	}
> +	return oxid_pool;
> +}
> +
>   inline void fnic_del_fabric_timer_sync(struct fnic *fnic)
>   {
>   	fnic->iport.fabric.del_timer_inprogress = 1;
> @@ -383,6 +484,56 @@ fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
>   				 "fabric timer is %d ", timeout);
>   }
>   
> +static void
> +fdls_start_tport_timer(struct fnic_iport_s *iport,
> +					   struct fnic_tport_s *tport, int timeout)
> +{
> +	u64 fabric_tov;
> +	struct fnic *fnic = iport->fnic;
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync(fnic, tport);
> +		tport->timer_pending = 0;
> +	}
> +
> +	if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED))
> +		tport->retry_counter++;
> +
> +	fabric_tov = jiffies + msecs_to_jiffies(timeout);
> +	mod_timer(&tport->retry_timer, round_jiffies(fabric_tov));
> +	tport->timer_pending = 1;
> +}
> +
> +void
> +fdls_send_tport_abts(struct fnic_iport_s *iport,
> +					 struct fnic_tport_s *tport)
> +{
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	struct fnic *fnic = iport->fnic;
> +	struct fc_frame_header tport_abort = fc_std_tport_abts;
> +	struct fc_frame_header *tport_abts = &tport_abort;
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +	FNIC_STD_SET_S_ID(tport_abts, s_id);
> +	FNIC_STD_SET_D_ID(tport_abts, d_id);
> +	tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
> +
> +	tport_abts->fh_ox_id = tport->oxid_used;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS sending tport abts: tport->state: %d ",
> +				 tport->state);
> +
> +	fnic_send_fcoe_frame(iport, tport_abts, sizeof(struct fc_frame_header));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
> +}
> +
>   static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
>   {
>   	uint8_t fcid[3];
> @@ -614,6 +765,176 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
>   	fdls_set_state((&iport->fabric), fdls_state);
>   }
>   
> +static void
> +fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fc_std_els_adisc adisc;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +
> +	memset(&adisc, 0, sizeof(struct fc_std_els_adisc));
> +	FNIC_STD_SET_R_CTL(&adisc.fchdr, 0x22);
> +	FNIC_STD_SET_TYPE(&adisc.fchdr, 0x01);
> +	FNIC_STD_SET_F_CTL(&adisc.fchdr, FNIC_ELS_REQ_FCTL << 16);
> +	FNIC_STD_SET_RX_ID(&adisc.fchdr, cpu_to_be16(0xFFFF));
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +	FNIC_STD_SET_S_ID(&adisc.fchdr, s_id);
> +	FNIC_STD_SET_D_ID(&adisc.fchdr, d_id);
> +
> +	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->adisc_oxid_pool));
> +	if (oxid == 0xFFFF) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Failed to allocate OXID to send ADISC %p", iport);
> +		return;
> +	}
> +
> +	tport->oxid_used = oxid;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +
> +	FNIC_STD_SET_OX_ID((&adisc.fchdr), oxid);
> +	FNIC_STD_SET_NPORT_NAME(&adisc.els.adisc_wwpn,
> +				le64_to_cpu(iport->wwpn));
> +	FNIC_STD_SET_NODE_NAME(&adisc.els.adisc_wwnn, le64_to_cpu(iport->wwnn));
> +
> +	memcpy(adisc.els.adisc_port_id, s_id, 3);
That feels wrong. Isn't port_id a three-byte value in big endian?

> +	adisc.els.adisc_cmd = ELS_ADISC;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "sending ADISC to tgt fcid: 0x%x", tport->fcid);
> +
> +
> +	fnic_send_fcoe_frame(iport, &adisc, sizeof(struct fc_std_els_adisc));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
> +}
> +
> +bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fnic_tport_event_s *tport_del_evt;
> +	struct fnic *fnic = iport->fnic;
> +
> +	if ((tport->state == FDLS_TGT_STATE_OFFLINING)
> +	    || (tport->state == FDLS_TGT_STATE_OFFLINE)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			     "tport fcid 0x%x: tport state is offlining/offline\n",
> +			     tport->fcid);
> +		return false;
> +	}
> +
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
> +	/*
> +	 * By setting this flag, the tport will not be seen in a look-up
> +	 * in an RSCN. Even if we move to multithreaded model, this tport
> +	 * will be destroyed and a new RSCN will have to create a new one
> +	 */
> +	tport->flags |= FNIC_FDLS_TPORT_TERMINATING;
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync(fnic, tport);
> +		tport->timer_pending = 0;
> +	}
> +
> +	if (IS_FNIC_FCP_INITIATOR(fnic)) {
> +		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
> +		fnic_rport_exch_reset(iport->fnic, tport->fcid);
> +		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
> +
> +		if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) {
> +			tport_del_evt =
> +				kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
> +			if (!tport_del_evt) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Failed to allocate memory for tport fcid: 0x%0x\n",
> +					 tport->fcid);
> +				return false;
> +			}
> +			tport_del_evt->event = TGT_EV_RPORT_DEL;
> +			tport_del_evt->arg1 = (void *) tport;
> +			list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
> +			queue_work(fnic_event_queue, &fnic->tport_work);
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "tport 0x%x not reg with scsi_transport. Freeing locally",
> +				 tport->fcid);
> +			list_del(&tport->links);
> +			kfree(tport);
> +		}
> +	}
> +	return true;
> +}
> +
> +static void
> +fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fc_std_flogi plogi;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +	uint32_t timeout;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Send tgt PLOGI to fcid: 0x%x", tport->fcid);
> +
> +	memcpy(&plogi, &fnic_std_plogi_req, sizeof(struct fc_std_flogi));
> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +
> +	FNIC_STD_SET_S_ID(&plogi.fchdr, s_id);
> +	FNIC_STD_SET_D_ID(&plogi.fchdr, d_id);
> +	FNIC_LOGI_SET_RDF_SIZE(&plogi.els, iport->max_payload_size);
> +
> +	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool));
> +	if (oxid == 0xFFFF) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
> +				 iport->fcid, tport->fcid);
> +		return;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "send tgt PLOGI: tgt fcid: 0x%x oxid: 0x%x", tport->fcid,
> +				 ntohs(oxid));
> +	tport->oxid_used = oxid;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +
> +	FNIC_STD_SET_OX_ID((&plogi.fchdr), oxid);
> +	FNIC_LOGI_SET_NPORT_NAME(&plogi.els, iport->wwpn);
> +	FNIC_LOGI_SET_NODE_NAME(&plogi.els, iport->wwnn);
> +
> +	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
> +
> +
> +	fnic_send_fcoe_frame(iport, &plogi, sizeof(struct fc_std_flogi));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, timeout);
> +}
> +
> +static uint16_t
> +fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
> +		      struct fc_std_flogi *plogi_rsp)
> +{
> +	uint16_t b2b_rdf_size =
> +	    be16_to_cpu(FNIC_LOGI_RDF_SIZE(&plogi_rsp->els));
> +	uint16_t spc3_rdf_size =
> +	    be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & FNIC_FC_C3_RDF;
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
> +			 b2b_rdf_size, spc3_rdf_size);
> +
> +	return MIN(b2b_rdf_size, spc3_rdf_size);
> +}
> +
>   static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
>   {
>   	struct fc_std_rft_id rft_id;
> @@ -690,6 +1011,48 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
>   	fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
>   }
>   
> +static void
> +fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fc_std_els_prli prli;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +	uint32_t timeout;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS sending PRLI to tgt: 0x%x", tport->fcid);
> +
> +	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->prli_oxid_pool));
> +	if (oxid == 0xFFFF) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Failed to allocate OXID to send PRLI %p", iport);
> +		return;
> +	}
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS sending PRLI to tgt: 0x%x OXID: 0x%x", tport->fcid,
> +				 ntohs(oxid));
> +
> +	tport->oxid_used = oxid;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +	memcpy(&prli, &fnic_std_prli_req, sizeof(struct fc_std_els_prli));
Memcpy again.

> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +
> +	FNIC_STD_SET_S_ID((&prli.fchdr), s_id);
> +	FNIC_STD_SET_D_ID((&prli.fchdr), d_id);
> +	FNIC_STD_SET_OX_ID((&prli.fchdr), oxid);
> +
> +	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
> +
> +	fdls_get_tgt_oxid_pool(tport);
> +	fnic_send_fcoe_frame(iport, &prli, sizeof(struct fc_std_els_prli));
> +	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
> +	fdls_start_tport_timer(iport, tport, timeout);
> +}
> +
>   /**
>    * fdls_send_fabric_logo - Send flogo to the fcf
>    * @iport: Handle to fnic iport
> @@ -742,6 +1105,212 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport)
>   	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo));
>   }
>   
> +/**
> + * fdls_tgt_logout - Send plogo to the remote port
> + * @iport: Handle to fnic iport
> + * @tport: Handle to remote port
> + *
> + * This function does not change or check the fabric/tport state.
> + * It the caller's responsibility to set the appropriate tport/fabric
> + * state when this is called. Normally that is fdls_tgt_state_plogo.
> + * This could be used to send plogo to nameserver process
> + * also not just target processes
> + */
> +void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
> +{
> +	struct fc_std_logo logo;
> +	uint8_t s_id[3];
> +	uint8_t d_id[3];
> +	struct fnic *fnic = iport->fnic;
> +	uint16_t oxid;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Sending logo to tport fcid: 0x%x", tport->fcid);
> +	memcpy(&logo, &fnic_std_logo_req, sizeof(struct fc_std_logo));
And here.

> +
> +	hton24(s_id, iport->fcid);
> +	hton24(d_id, tport->fcid);
> +
> +	FNIC_STD_SET_S_ID((&logo.fchdr), s_id);
> +	FNIC_STD_SET_D_ID((&logo.fchdr), d_id);
> +
> +	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool));
> +	FNIC_STD_SET_OX_ID((&logo.fchdr), oxid);
> +
> +	memcpy(&logo.els.fl_n_port_id, s_id, 3);
> +	FNIC_STD_SET_NPORT_NAME(&logo.els.fl_n_port_wwn,
> +				le64_to_cpu(iport->wwpn));
> +
> +
> +	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo));
> +}
> +
> +static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
> +{
> +	struct fnic_tport_s *tport, *next;
> +	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: Starting FDLS target discovery", iport->fcid);
> +
> +	list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
> +		if ((old_link_down_cnt != iport->fnic->link_down_cnt)
> +			|| (iport->state != FNIC_IPORT_STATE_READY)) {
> +			break;
> +		}
> +		/* if we marked the tport as deleted due to GPN_FT
> +		 * We should not send ADISC anymore
> +		 */
> +		if ((tport->state == FDLS_TGT_STATE_OFFLINING) ||
> +			(tport->state == FDLS_TGT_STATE_OFFLINE))
> +			continue;
> +
> +		/* For tports which have received RSCN */
> +		if (tport->flags & FNIC_FDLS_TPORT_SEND_ADISC) {
> +			tport->retry_counter = 0;
> +			fdls_set_tport_state(tport, FDLS_TGT_STATE_ADISC);
> +			tport->flags &= ~FNIC_FDLS_TPORT_SEND_ADISC;
> +			fdls_send_tgt_adisc(iport, tport);
> +			continue;
> +		}
> +		if (fdls_get_tport_state(tport) != FDLS_TGT_STATE_INIT) {
> +			/* Not a new port, skip  */
> +			continue;
> +		}
> +		tport->retry_counter = 0;
> +		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
> +		fdls_send_tgt_plogi(iport, tport);
> +	}
> +	fdls_set_state((&iport->fabric), FDLS_STATE_TGT_DISCOVERY);
> +}
> +
> +/*
> + * Function to restart the IT nexus if we received any out of
> + * sequence PLOGI/PRLI  response from the target.
> + * The memory for the new tport structure is allocated
> + * inside fdls_create_tport and added to the iport's tport list.
> + * This will get freed later during tport_offline/linkdown
> + * or module unload. The new_tport pointer will go out of scope
> + * safely since the memory it is
> + * pointing to it will be freed later
> + */
> +static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
> +{
> +	struct fnic_iport_s *iport = tport->iport;
> +	struct fnic_tport_s *new_tport = NULL;
> +	uint32_t fcid;
> +	uint64_t wwpn;
> +	int nexus_restart_count;
> +	struct fnic *fnic = iport->fnic;
> +	bool retval = true;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "tport fcid: 0x%x state: %d restart_count: %d",
> +				 tport->fcid, tport->state, tport->nexus_restart_count);
> +
> +	fcid = tport->fcid;
> +	wwpn = tport->wwpn;
> +	nexus_restart_count = tport->nexus_restart_count;
> +
> +	retval = fdls_delete_tport(iport, tport);
> +	if (retval != true) {
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +			     "Error deleting tport: 0x%x", fcid);
> +		return;
> +	}
> +
> +	if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			     "Exceeded nexus restart retries tport: 0x%x",
> +			     fcid);
> +		return;
> +	}
> +
> +	/*
> +	 * Allocate memory for the new tport and add it to
> +	 * iport's tport list.
> +	 * This memory will be freed during tport_offline/linkdown
> +	 * or module unload. The pointer new_tport is safe to go
> +	 * out of scope when this function returns, since the memory
> +	 * it is pointing to is guaranteed to be freed later
> +	 * as mentioned above.
> +	 */
> +	new_tport = fdls_create_tport(iport, fcid, wwpn);
> +	if (!new_tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Error creating new tport: 0x%x", fcid);
> +		return;
> +	}
> +
> +	new_tport->nexus_restart_count = nexus_restart_count + 1;
> +	fdls_send_tgt_plogi(iport, new_tport);
> +	fdls_set_tport_state(new_tport, FDLS_TGT_STATE_PLOGI);
> +}
> +
> +struct fnic_tport_s *fnic_find_tport_by_fcid(struct fnic_iport_s *iport,
> +									 uint32_t fcid)
> +{
> +	struct fnic_tport_s *tport, *next;
> +
> +	list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
> +		if ((tport->fcid == fcid)
> +			&& !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
> +			return tport;

Odd. Can you have two entries with the same fcid in the list?

> +	}
> +	return NULL;
> +}
> +
> +static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
> +								  uint32_t fcid, uint64_t wwpn)
> +{
> +	struct fnic_tport_s *tport;
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);
> +
> +	tport = kzalloc(sizeof(struct fnic_tport_s), GFP_ATOMIC);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Memory allocation failure while creating tport: 0x%x\n",
> +			 fcid);
> +		return NULL;
> +	}
> +
> +	tport->max_payload_size = FNIC_FCOE_MAX_FRAME_SZ;
> +	tport->r_a_tov = FNIC_R_A_TOV_DEF;
> +	tport->e_d_tov = FNIC_E_D_TOV_DEF;
> +	tport->fcid = fcid;
> +	tport->wwpn = wwpn;
> +	tport->iport = iport;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Need to setup tport timer callback");
> +
> +	timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Added tport 0x%x", tport->fcid);
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
> +	list_add_tail(&tport->links, &iport->tport_list);
> +	atomic_set(&tport->in_flight, 0);
> +	return tport;
> +}
> +
> +struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
> +									 uint64_t wwpn)
> +{
> +	struct fnic_tport_s *tport, *next;
> +
> +	list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
> +		if ((tport->wwpn == wwpn)
> +			&& !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
> +			return tport;
Same argument here.
I would have expected wwpns to be unique ...

> +	}
> +	return NULL;
> +}
> +
>   void fdls_fabric_timer_callback(struct timer_list *t)
>   {
>   	struct fnic_fdls_fabric_s *fabric = from_timer(fabric, t, retry_timer);
> @@ -847,90 +1416,588 @@ void fdls_fabric_timer_callback(struct timer_list *t)
>   			/* ABTS has timed out */
>   			fdls_schedule_fabric_oxid_free(iport);
>   			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -						 "ABTS timed out. Starting PLOGI: %p", iport);
> -			fnic_fdls_start_plogi(iport);
> +						 "ABTS timed out. Starting PLOGI: %p", iport);
> +			fnic_fdls_start_plogi(iport);
> +		}
> +		break;
> +	case FDLS_STATE_REGISTER_FC4_TYPES:
> +		/* scr received a LS_RJT with busy we retry from here */
> +		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> +			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_register_fc4_types(iport);
> +		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
> +			/* RFT_ID timed out send abts */
> +			fdls_send_fabric_abts(iport);
> +		} else {
> +			/* ABTS has timed out */
> +			fdls_schedule_fabric_oxid_free(iport);
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"ABTS timed out. Starting PLOGI: %p", iport);
> +			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
> +		}
> +		break;
> +	case FDLS_STATE_REGISTER_FC4_FEATURES:
> +		/* scr received a LS_RJT with busy we retry from here */
> +		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> +			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_register_fc4_features(iport);
> +		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
> +			/* SCR has timed out. Send abts */
> +			fdls_send_fabric_abts(iport);
> +		else {
> +			/* ABTS has timed out */
> +			fdls_schedule_fabric_oxid_free(iport);
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"ABTS timed out. Starting PLOGI %p", iport);
> +			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
> +		}
> +		break;
> +	case FDLS_STATE_RSCN_GPN_FT:
> +	case FDLS_STATE_SEND_GPNFT:
> +	case FDLS_STATE_GPN_FT:
> +		/* GPN_FT received a LS_RJT with busy we retry from here */
> +		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> +			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_gpn_ft(iport, iport->fabric.state);
> +		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
> +			/* gpn_ft has timed out. Send abts */
> +			fdls_send_fabric_abts(iport);
> +		} else {
> +			/* ABTS has timed out */
> +			fdls_schedule_fabric_oxid_free(iport);
> +			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
> +				fdls_send_gpn_ft(iport, iport->fabric.state);
> +			} else {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
> +					 iport);
> +			}
> +		}
> +		break;
> +	default:
> +		fnic_fdls_start_flogi(iport);	/* Placeholder call */
> +		break;
> +	}
> +	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +}
> +
> +static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
> +{
> +	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
> +	struct fnic *fnic = iport->fnic;
> +	struct fnic_tport_event_s *tport_del_evt;
> +
> +	if (!IS_FNIC_FCP_INITIATOR(fnic))
> +		return;
> +
> +	tport_del_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
> +	if (!tport_del_evt) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Failed to allocate memory for tport event fcid: 0x%x",
> +			 tport->fcid);
> +		return;
> +	}
> +	tport_del_evt->event = TGT_EV_TPORT_DELETE;
> +	tport_del_evt->arg1 = (void *) tport;
> +	list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
> +	queue_work(fnic_event_queue, &fnic->tport_work);
> +}
> +
> +static void fdls_tport_timer_callback(struct timer_list *t)
> +{
> +	struct fnic_tport_s *tport = from_timer(tport, t, retry_timer);
> +	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
> +	struct fnic *fnic = iport->fnic;
> +	uint16_t oxid;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&fnic->fnic_lock, flags);
> +	if (!tport->timer_pending) {
> +		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		return;
> +	}
> +
> +	if (iport->state != FNIC_IPORT_STATE_READY) {
> +		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		return;
> +	}
> +
> +	if (tport->del_timer_inprogress) {
> +		tport->del_timer_inprogress = 0;
> +		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n",
> +			 tport->fcid);
> +		return;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +		 "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d",
> +		 tport->fcid, tport->timer_pending, tport->state,
> +		 tport->retry_counter);
> +
> +	tport->timer_pending = 0;
> +	oxid = ntohs(tport->oxid_used);
> +
> +	/* We retry plogi/prli/adisc frames depending on the tport state */
> +	switch (tport->state) {
> +	case FDLS_TGT_STATE_PLOGI:
> +		/* PLOGI frame received a LS_RJT with busy, we retry from here */
> +		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (tport->retry_counter < iport->max_plogi_retries)) {
> +			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_tgt_plogi(iport, tport);
> +		} else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +			/* Plogi frame has timed out, send abts */
> +			fdls_send_tport_abts(iport, tport);
> +		} else if (tport->retry_counter < iport->max_plogi_retries) {
> +			/*
> +			 * ABTS has timed out
> +			 */
> +			fdls_schedule_tgt_oxid_free(iport,
> +						    &iport->plogi_oxid_pool,
> +						    oxid);
> +			fdls_send_tgt_plogi(iport, tport);
> +		} else {
> +			/* exceeded plogi retry count */
> +			fdls_schedule_tgt_oxid_free(iport,
> +						    &iport->plogi_oxid_pool,
> +						    oxid);
> +			fdls_send_delete_tport_msg(tport);
> +		}
> +		break;
> +	case FDLS_TGT_STATE_PRLI:
> +		/* PRLI received a LS_RJT with busy , hence we retry from here */
> +		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
> +			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
> +			fdls_send_tgt_prli(iport, tport);
> +		} else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +			/* PRLI has time out, send abts */
> +			fdls_send_tport_abts(iport, tport);
> +		} else {
> +			/* ABTS has timed out for prli, we go back to PLOGI */
> +			fdls_schedule_tgt_oxid_free(iport,
> +						    &iport->prli_oxid_pool,
> +						    oxid);
> +			fdls_send_tgt_plogi(iport, tport);
> +			fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
> +		}
> +		break;
> +	case FDLS_TGT_STATE_ADISC:
> +		/* ADISC timed out send an ABTS */
> +		if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +			fdls_send_tport_abts(iport, tport);
> +		} else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)
> +				   && (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +			/*
> +			 * ABTS has timed out
> +			 */
> +			fdls_schedule_tgt_oxid_free(iport,
> +						    &iport->adisc_oxid_pool,
> +						    oxid);
> +			fdls_send_tgt_adisc(iport, tport);
> +		} else {
> +			/* exceeded retry count */
> +			fdls_schedule_tgt_oxid_free(iport,
> +						    &iport->adisc_oxid_pool,
> +						    oxid);
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "ADISC not responding. Deleting target port: 0x%x",
> +					 tport->fcid);
> +			fdls_send_delete_tport_msg(tport);
> +		}
> +		break;
> +	default:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Unknown tport state: 0x%x", tport->state);
> +		break;
> +	}
> +	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> +}
> +
> +static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
> +{
> +	iport->fabric.retry_counter = 0;
> +	fdls_send_fabric_flogi(iport);
> +	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
> +	iport->fabric.flags = 0;
> +}
> +
> +static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
> +{
> +	iport->fabric.retry_counter = 0;
> +	fdls_send_fabric_plogi(iport);
> +	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
> +	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
> +}
> +
> +static void
> +fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
> +			   struct fc_frame_header *fchdr)
> +{
> +	uint32_t tgt_fcid;
> +	struct fnic_tport_s *tport;
> +	uint8_t *fcid;
> +	uint64_t frame_wwnn;
> +	uint64_t frame_wwpn;
> +	uint16_t oxid;
> +	struct fc_std_els_adisc *adisc_rsp = (struct fc_std_els_adisc *)fchdr;
> +	struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
> +	struct fnic *fnic = iport->fnic;
> +
> +	fcid = FNIC_STD_GET_S_ID(fchdr);
> +	tgt_fcid = ntoh24(fcid);
> +	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
> +
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Tgt ADISC response tport not found: 0x%x", tgt_fcid);
> +		return;
> +	}
> +	if ((iport->state != FNIC_IPORT_STATE_READY)
> +		|| (tport->state != FDLS_TGT_STATE_ADISC)
> +		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Dropping this ADISC response");
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "iport state: %d tport state: %d Is abort issued on PRLI? %d",
> +			 iport->state, tport->state,
> +			 (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED));
> +		return;
> +	}
> +	if (ntohs(fchdr->fh_ox_id) != ntohs(tport->oxid_used)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Dropping frame from target: 0x%x",
> +			 tgt_fcid);
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery");
> +		return;
> +	}
> +
> +	oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
> +	fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, oxid);
> +
> +	switch (adisc_rsp->els.adisc_cmd) {
> +	case ELS_LS_ACC:
> +		if (tport->timer_pending) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "tport 0x%p Canceling fabric disc timer\n",
> +						 tport);
> +			fnic_del_tport_timer_sync(fnic, tport);
> +		}
> +		tport->timer_pending = 0;
> +		tport->retry_counter = 0;
> +		frame_wwnn = get_unaligned_be64(&adisc_rsp->els.adisc_wwnn);
> +		frame_wwpn = get_unaligned_be64(&adisc_rsp->els.adisc_wwpn);
> +		if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "ADISC accepted from target: 0x%x. Target logged in",
> +				 tgt_fcid);
> +			fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "Error mismatch frame: ADISC");
> +		}
> +		break;
> +
> +	case ELS_LS_RJT:
> +		if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
> +		     || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
> +			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "ADISC ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
> +				 tgt_fcid);
> +
> +			/* Retry ADISC again from the timer routine. */
> +			tport->flags |= FNIC_FDLS_RETRY_FRAME;
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "ADISC returned ELS_LS_RJT from target: 0x%x",
> +						 tgt_fcid);
> +			fdls_delete_tport(iport, tport);
> +		}
> +		break;
> +	}
> +}
> +
> +
> +static void
> +fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
> +			   struct fc_frame_header *fchdr)
> +{
> +	uint32_t tgt_fcid;
> +	struct fnic_tport_s *tport;
> +	uint8_t *fcid;
> +	uint16_t oxid;
> +	struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *)fchdr;
> +	struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
> +	int max_payload_size;
> +	struct fnic *fnic = iport->fnic;
> +
> +	fcid = FNIC_STD_GET_S_ID(fchdr);
> +	tgt_fcid = ntoh24(fcid);
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS processing target PLOGI response: tgt_fcid: 0x%x",
> +				 tgt_fcid);
> +
> +	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport not found: 0x%x", tgt_fcid);
> +		return;
> +	}
> +	if ((iport->state != FNIC_IPORT_STATE_READY)
> +		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Dropping frame! iport state: %d tport state: %d",
> +					 iport->state, tport->state);
> +		return;
> +	}
> +
> +	if (tport->state != FDLS_TGT_STATE_PLOGI) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			     "PLOGI rsp recvd in wrong state. Drop the frame and restart nexus");
> +		fdls_target_restart_nexus(tport);
> +		return;
> +	}
> +
> +	if (fchdr->fh_ox_id != tport->oxid_used) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "PLOGI response from target: 0x%x. Dropping frame",
> +			 tgt_fcid);
> +		return;
> +	}
> +
> +	oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
> +	fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, oxid);
> +
> +	switch (plogi_rsp->els.fl_cmd) {
> +	case ELS_LS_ACC:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PLOGI accepted by target: 0x%x", tgt_fcid);
> +		break;
> +
> +	case ELS_LS_RJT:
> +		if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
> +		     || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
> +			&& (tport->retry_counter < iport->max_plogi_retries)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "PLOGI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
> +				 tgt_fcid);
> +			/* Retry plogi again from the timer routine. */
> +			tport->flags |= FNIC_FDLS_RETRY_FRAME;
> +			return;
> +		}
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PLOGI returned ELS_LS_RJT from target: 0x%x",
> +					 tgt_fcid);
> +		fdls_delete_tport(iport, tport);
> +		return;
> +
> +	default:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PLOGI not accepted from target fcid: 0x%x",
> +					 tgt_fcid);
> +		return;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Found the PLOGI target: 0x%x and state: %d",
> +				 (unsigned int) tgt_fcid, tport->state);
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync(fnic, tport);
> +	}
> +
> +	tport->timer_pending = 0;
> +	tport->wwpn = get_unaligned_be64(&FNIC_LOGI_PORT_NAME(&plogi_rsp->els));
> +	tport->wwnn = get_unaligned_be64(&FNIC_LOGI_NODE_NAME(&plogi_rsp->els));
> +
> +	/* Learn the Service Params */
> +
> +	/* Max frame size - choose the lowest */
> +	max_payload_size = fnic_fc_plogi_rsp_rdf(iport, plogi_rsp);
> +	tport->max_payload_size =
> +		MIN(max_payload_size, iport->max_payload_size);
> +
> +	if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "MFS: tport max frame size below spec bounds: %d",
> +			 tport->max_payload_size);
> +		tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +		 "MAX frame size: %d iport max_payload_size: %d tport mfs: %d",
> +		 max_payload_size, iport->max_payload_size,
> +		 tport->max_payload_size);
> +
> +	tport->max_concur_seqs = FNIC_FC_PLOGI_RSP_CONCUR_SEQ(plogi_rsp);
> +
> +	tport->retry_counter = 0;
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_PRLI);
> +	fdls_send_tgt_prli(iport, tport);
> +}
> +
> +static void
> +fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
> +			  struct fc_frame_header *fchdr)
> +{
> +	uint32_t tgt_fcid;
> +	struct fnic_tport_s *tport;
> +	uint8_t *fcid;
> +	uint16_t oxid;
> +	struct fc_std_els_prli *prli_rsp = (struct fc_std_els_prli *)fchdr;
> +	struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
> +	struct fnic_tport_event_s *tport_add_evt;
> +	struct fnic *fnic = iport->fnic;
> +	bool mismatched_tgt = false;
> +
> +	fcid = FNIC_STD_GET_S_ID(fchdr);
> +	tgt_fcid = ntoh24(fcid);
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "FDLS process tgt PRLI response: 0x%x", tgt_fcid);
> +
> +	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport not found: 0x%x", tgt_fcid);
> +		/* Handle or just drop? */
> +		return;
> +	}
> +
> +	if ((iport->state != FNIC_IPORT_STATE_READY)
> +		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x",
> +			 iport->state, tport->state, tport->fcid);
> +		return;
> +	}
> +
> +	if (tport->state != FDLS_TGT_STATE_PRLI) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			     "PRLI rsp recvd in wrong state. Drop frame. Restarting nexus");
> +		fdls_target_restart_nexus(tport);
> +		return;
> +	}
> +
> +	if (fchdr->fh_ox_id != tport->oxid_used) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Dropping PRLI response from target: 0x%x ",
> +			 tgt_fcid);
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery");
> +		return;
> +	}
> +
> +	oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
> +	fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
> +
> +	switch (prli_rsp->els_prli.prli_cmd) {
> +	case ELS_LS_ACC:
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PRLI accepted from target: 0x%x", tgt_fcid);
> +
> +		if (prli_rsp->sp.spp_type != FC_FC4_TYPE_SCSI) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "mismatched target zoned with FC SCSI initiator: 0x%x",
> +				 tgt_fcid);
> +			mismatched_tgt = true;
>   		}
> -		break;
> -	case FDLS_STATE_REGISTER_FC4_TYPES:
> -		/* scr received a LS_RJT with busy we retry from here */
> -		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> -			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> -			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> -			fdls_send_register_fc4_types(iport);
> -		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
> -			/* RFT_ID timed out send abts */
> -			fdls_send_fabric_abts(iport);
> -		} else {
> -			/* ABTS has timed out */
> -			fdls_schedule_fabric_oxid_free(iport);
> -			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -						 "ABTS timed out. Starting PLOGI: %p", iport);
> -			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
> +		if (mismatched_tgt) {
> +			fdls_tgt_logout(iport, tport);
> +			fdls_delete_tport(iport, tport);
> +			return;
>   		}
>   		break;
> -	case FDLS_STATE_REGISTER_FC4_FEATURES:
> -		/* scr received a LS_RJT with busy we retry from here */
> -		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> -			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> -			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> -			fdls_send_register_fc4_features(iport);
> -		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
> -			/* SCR has timed out. Send abts */
> -			fdls_send_fabric_abts(iport);
> -		else {
> -			/* ABTS has timed out */
> -			fdls_schedule_fabric_oxid_free(iport);
> +	case ELS_LS_RJT:
> +		if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
> +		     || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
> +			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
> +
>   			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -						 "ABTS timed out. Starting PLOGI %p", iport);
> -			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
> -		}
> -		break;
> -	case FDLS_STATE_RSCN_GPN_FT:
> -	case FDLS_STATE_SEND_GPNFT:
> -	case FDLS_STATE_GPN_FT:
> -		/* GPN_FT received a LS_RJT with busy we retry from here */
> -		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
> -			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
> -			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
> -			fdls_send_gpn_ft(iport, iport->fabric.state);
> -		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
> -			/* gpn_ft has timed out. Send abts */
> -			fdls_send_fabric_abts(iport);
> +				 "PRLI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
> +				 tgt_fcid);
> +
> +			/*Retry Plogi again from the timer routine. */
> +			tport->flags |= FNIC_FDLS_RETRY_FRAME;
> +			return;
>   		} else {
> -			/* ABTS has timed out */
> -			fdls_schedule_fabric_oxid_free(iport);
> -			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
> -				fdls_send_gpn_ft(iport, iport->fabric.state);
> -			} else {
> -				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
> -					 iport);
> -			}
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "PRLI returned ELS_LS_RJT from target: 0x%x",
> +						 tgt_fcid);
> +
> +			fdls_tgt_logout(iport, tport);
> +			fdls_delete_tport(iport, tport);
> +			return;
>   		}
>   		break;
> +
>   	default:
> -		fnic_fdls_start_flogi(iport);	/* Placeholder call */
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "PRLI not accepted from target: 0x%x", tgt_fcid);
> +		return;
>   		break;
>   	}
> -	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
> -}
>   
> -static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
> -{
> -	iport->fabric.retry_counter = 0;
> -	fdls_send_fabric_flogi(iport);
> -	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
> -	iport->fabric.flags = 0;
> -}
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Found the PRLI target: 0x%x and state: %d",
> +				 (unsigned int) tgt_fcid, tport->state);
>   
> -static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
> -{
> -	iport->fabric.retry_counter = 0;
> -	fdls_send_fabric_plogi(iport);
> -	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
> -	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "tport fcid 0x%x: Canceling disc timer\n",
> +					 tport->fcid);
> +		fnic_del_tport_timer_sync(fnic, tport);
> +	}
> +	tport->timer_pending = 0;
> +
> +	/* Learn Service Params */
> +	tport->fcp_csp = be32_to_cpu(prli_rsp->sp.spp_params);
> +	tport->retry_counter = 0;
> +
> +	if (prli_rsp->sp.spp_params & FCP_SPPF_RETRY)
> +		tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
> +
> +	/* Check if the device plays Target Mode Function */
> +	if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "Remote port(0x%x): no target support. Deleting it\n",
> +			 tgt_fcid);
> +		fdls_tgt_logout(iport, tport);
> +		fdls_delete_tport(iport, tport);
> +		return;
> +	}
> +
> +	fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
> +
> +	/* Inform the driver about new target added */
> +	tport_add_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
> +	if (!tport_add_evt) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "tport event memory allocation failure: 0x%0x\n",
> +				 tport->fcid);
> +		return;
> +	}
> +	tport_add_evt->event = TGT_EV_RPORT_ADD;
> +	tport_add_evt->arg1 = (void *) tport;
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "iport fcid: 0x%x add tport event fcid: 0x%x\n",
> +			 tport->fcid, iport->fcid);
> +	list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
> +	queue_work(fnic_event_queue, &fnic->tport_work);
>   }
>   
> +
>   static void
>   fdls_process_rff_id_rsp(struct fnic_iport_s *iport,
>   			struct fc_frame_header *fchdr)
> @@ -1179,7 +2246,8 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport,
>   						 iport);
>   			if (iport->fabric.timer_pending) {
>   				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> -							 "Canceling fabric disc timer %p\n", iport);
> +					     "Canceling fabric disc timer %p\n",
> +					     iport);
>   				fnic_del_fabric_timer_sync(fnic);
>   			}
>   			fdls->timer_pending = 0;
> @@ -1192,6 +2260,104 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport,
>   	}
>   }
>   
> +static void
> +fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
> +			     struct fc_frame_header *fchdr, int len)
> +{
> +	struct fc_gpn_ft_rsp_iu *gpn_ft_tgt;
> +	struct fnic_tport_s *tport, *next;
> +	uint32_t fcid;
> +	uint64_t wwpn;
> +	int rem_len = len;
> +	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
> +	struct fnic *fnic = iport->fnic;
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
> +
> +	gpn_ft_tgt =
> +	    (struct fc_gpn_ft_rsp_iu *)((uint8_t *) fchdr +
> +					sizeof(struct fc_frame_header)
> +					+ sizeof(struct fc_ct_hdr));
> +	len -= sizeof(struct fc_frame_header) + sizeof(struct fc_ct_hdr);
> +
> +	while (rem_len > 0) {
> +
> +		fcid = ntoh24(gpn_ft_tgt->fcid);
> +		wwpn = ntohll(gpn_ft_tgt->wwpn);
> +
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
> +
> +		if (fcid == iport->fcid) {
> +			if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
> +				break;
> +			gpn_ft_tgt++;
> +			rem_len -= sizeof(struct fc_gpn_ft_rsp_iu);
> +			continue;
> +		}
> +
> +		tport = fnic_find_tport_by_wwpn(iport, wwpn);
> +		if (!tport) {
> +			/*
> +			 * New port registered with the switch or first time query
> +			 */
> +			tport = fdls_create_tport(iport, fcid, wwpn);
> +			if (!tport)
> +				return;
> +		}
> +		/*
> +		 * check if this was an existing tport with same fcid
> +		 * but whose wwpn has changed now ,then remove it and
> +		 * create a new one
> +		 */
> +		if (tport->fcid != fcid) {
> +			fdls_delete_tport(iport, tport);
> +			tport = fdls_create_tport(iport, fcid, wwpn);
> +			if (!tport)
> +				return;
> +		}
> +
> +		/*
> +		 * If this GPN_FT rsp is after RSCN then mark the tports which
> +		 * matches with the new GPN_FT list, if some tport is not
> +		 * found in GPN_FT we went to delete that tport later.
> +		 */
> +		if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT)
> +			tport->flags |= FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
> +
> +		if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
> +			break;
> +
> +		gpn_ft_tgt++;
> +		rem_len -= sizeof(struct fc_gpn_ft_rsp_iu);
> +	}
> +	if (rem_len <= 0) {
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +			 "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
> +			 len, rem_len);
> +	}
> +
> +	/*remove those ports which was not listed in GPN_FT */
> +	if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT) {
> +		list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
> +
> +			if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Remove port: 0x%x not found in GPN_FT list",
> +					 tport->fcid);
> +				fdls_delete_tport(iport, tport);
> +			} else {
> +				tport->flags &= ~FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
> +			}
> +			if ((old_link_down_cnt != iport->fnic->link_down_cnt)
> +				|| (iport->state != FNIC_IPORT_STATE_READY)) {
> +				return;
> +			}
> +		}
> +	}
> +}
> +
>   static void
>   fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
>   			struct fc_frame_header *fchdr, int len)
> @@ -1200,6 +2366,9 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
>   	struct fc_std_gpn_ft *gpn_ft_rsp = (struct fc_std_gpn_ft *) fchdr;
>   	uint16_t rsp;
>   	uint8_t reason_code;
> +	int count = 0;
> +	struct fnic_tport_s *tport, *next;
> +	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
>   	struct fnic *fnic = iport->fnic;
>   
>   	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> @@ -1239,12 +2408,74 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
>   	case FC_FS_ACC:
>   		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
>   					 "0x%x: GPNFT_RSP accept", iport->fcid);
> +		if (iport->fabric.timer_pending) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "0x%x: Canceling fabric disc timer\n",
> +						 iport->fcid);
> +			fnic_del_fabric_timer_sync(fnic);
> +		}
> +		iport->fabric.timer_pending = 0;
> +		iport->fabric.retry_counter = 0;
> +		fdls_process_gpn_ft_tgt_list(iport, fchdr, len);
> +
> +		/*
> +		 * iport state can change only if link down event happened
> +		 * We don't need to undo fdls_process_gpn_ft_tgt_list,
> +		 * that will be taken care in next link up event
> +		 */
> +		if (iport->state != FNIC_IPORT_STATE_READY) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Halting target discovery: fab st: %d iport st: %d ",
> +				 fdls_get_state(fdls), iport->state);
> +			break;
> +		}
> +		fdls_tgt_discovery_start(iport);
>   		break;
>   
>   	case FC_FS_RJT:
>   		reason_code = gpn_ft_rsp->fc_std_ct_hdr.ct_reason;
>   		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
>   			 "0x%x: GPNFT_RSP Reject reason: %d", iport->fcid, reason_code);
> +
> +		if (((reason_code == FC_FS_RJT_BSY)
> +		     || (reason_code == FC_FS_RJT_UNABL))
> +			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine",
> +				 iport->fcid);
> +			/* Retry again from the timer routine */
> +			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
> +		} else {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "0x%x: GPNFT_RSP reject", iport->fcid);
> +			if (iport->fabric.timer_pending) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +							 "0x%x: Canceling fabric disc timer\n",
> +							 iport->fcid);
> +				fnic_del_fabric_timer_sync(fnic);
> +			}
> +			iport->fabric.timer_pending = 0;
> +			iport->fabric.retry_counter = 0;
> +			/*
> +			 * If GPN_FT ls_rjt then we should delete
> +			 * all existing tports
> +			 */
> +			count = 0;
> +			list_for_each_entry_safe(tport, next, &iport->tport_list,
> +									 links) {
> +				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +							 "GPN_FT_REJECT: Remove port: 0x%x",
> +							 tport->fcid);
> +				fdls_delete_tport(iport, tport);
> +				if ((old_link_down_cnt != iport->fnic->link_down_cnt)
> +					|| (iport->state != FNIC_IPORT_STATE_READY)) {
> +					return;
> +				}
> +				count++;
> +			}
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +						 "GPN_FT_REJECT: Removed (0x%x) ports", count);
> +		}
>   		break;
>   
>   	default:
> @@ -1494,8 +2725,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
>   			     struct fc_frame_header *fchdr)
>   {
>   	uint32_t s_id;
> -	struct fc_std_abts_ba_acc *ba_acc =
> -	(struct fc_std_abts_ba_acc *) fchdr;
> +	struct fc_std_abts_ba_acc *ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
>   	struct fc_std_abts_ba_rjt *ba_rjt;
>   	uint32_t fabric_state = iport->fabric.state;
>   	struct fnic *fnic = iport->fnic;
> @@ -1660,6 +2890,148 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
>   	}
>   }
>   
> +static void
> +fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
> +			  struct fc_frame_header *fchdr)
> +{
> +	uint32_t s_id;
> +	struct fnic_tport_s *tport;
> +	uint32_t tport_state;
> +	struct fc_std_abts_ba_acc *ba_acc;
> +	struct fc_std_abts_ba_rjt *ba_rjt;
> +	uint16_t oxid;
> +	struct fnic *fnic = iport->fnic;
> +
> +	s_id = ntoh24(fchdr->fh_s_id);
> +	ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
> +	ba_rjt = (struct fc_std_abts_ba_rjt *)fchdr;
> +
> +	tport = fnic_find_tport_by_fcid(iport, s_id);
> +	if (!tport) {
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "Received tgt abts rsp with invalid SID: 0x%x", s_id);
> +		return;
> +	}
> +
> +	if (tport->timer_pending) {
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "tport 0x%p Canceling fabric disc timer\n", tport);
> +		fnic_del_tport_timer_sync(fnic, tport);
> +	}
> +	if (iport->state != FNIC_IPORT_STATE_READY) {
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "Received tgt abts rsp in iport state(%d). Dropping.",
> +					 iport->state);
> +		return;
> +	}
> +	tport->timer_pending = 0;
> +	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
> +	tport_state = tport->state;
> +	oxid = ntohs(fchdr->fh_ox_id);
> +
> +	/*This abort rsp is for ADISC */
> +	if ((oxid >= FDLS_ADISC_OXID_BASE) && (oxid < FDLS_TGT_OXID_POOL_END)) {
> +		if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) {
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +				     "OX_ID: 0x%x tgt_fcid: 0x%x rcvd tgt adisc abts resp BA_ACC",
> +				     be16_to_cpu(ba_acc->acc.ba_ox_id),
> +				     tport->fcid);
> +		} else if (fchdr->fh_r_ctl == FNIC_BA_RJT_RCTL) {
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +				 "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ",
> +				 tport->fcid, tport_state);
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +				 "reason code: 0x%x reason code explanation:0x%x ",
> +				     ba_rjt->rjt.br_reason,
> +				     ba_rjt->rjt.br_explan);
> +		}
> +		if ((tport->retry_counter < FDLS_RETRY_COUNT)
> +		    && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
> +			fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool,
> +					   oxid);
> +			fdls_send_tgt_adisc(iport, tport);
> +			return;
> +		}
> +
> +		fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, oxid);
> +		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +					 "ADISC not responding. Deleting target port: 0x%x",
> +					 tport->fcid);
> +		fdls_delete_tport(iport, tport);
> +		if ((iport->state == FNIC_IPORT_STATE_READY)
> +			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
> +			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
> +			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
> +		}
> +		/*Restart a discovery of targets */
> +		return;
> +	}
> +
> +	/*This abort rsp is for PLOGI */
> +	if ((oxid >= FDLS_PLOGI_OXID_BASE) && (oxid < FDLS_PRLI_OXID_BASE)) {
> +		if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) {
> +			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
> +				 "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x",
> +				 tport->fcid);
> +		} else if (fchdr->fh_r_ctl == FNIC_BA_RJT_RCTL) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x",
> +				     tport->fcid, fchdr->fh_ox_id);
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "reason code: 0x%x reason code explanation: 0x%x",
> +				     ba_rjt->rjt.br_reason,
> +				     ba_rjt->rjt.br_explan);
> +		}
> +		if ((tport->retry_counter < iport->max_plogi_retries)
> +		    && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
> +			fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool,
> +					   oxid);
> +			fdls_send_tgt_plogi(iport, tport);
> +			return;
> +		}
> +
> +		fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, oxid);
> +		fdls_delete_tport(iport, tport);
> +		/*Restart a discovery of targets */
> +		if ((iport->state == FNIC_IPORT_STATE_READY)
> +			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
> +			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
> +			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
> +		}
> +		return;
> +	}
> +
> +	/*This abort rsp is for PRLI */
> +	if ((oxid >= FDLS_PRLI_OXID_BASE) && (oxid < FDLS_ADISC_OXID_BASE)) {
> +		if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "0x%x: Received tgt PRLI abts response BA_ACC",
> +				 tport->fcid);
> +		} else if (fchdr->fh_r_ctl == FNIC_BA_RJT_RCTL) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ",
> +				     tport->fcid, fchdr->fh_ox_id);
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "reason code: 0x%x reason code explanation: 0x%x",
> +				     ba_rjt->rjt.br_reason,
> +				     ba_rjt->rjt.br_explan);
> +		}
> +		if ((tport->retry_counter < FDLS_RETRY_COUNT)
> +		    && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
> +			fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
> +			fdls_send_tgt_prli(iport, tport);
> +			return;
> +		}
> +		fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
> +		fdls_send_tgt_plogi(iport, tport);	/* go back to plogi */
> +		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
> +		return;
> +	}
> +
> +	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				 "Received ABTS response for unknown frame %p", iport);
> +}
> +
>   /*
>    * Performs a validation for all FCOE frames and return the frame type
>    */
> @@ -1757,6 +3129,39 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
>   		}
>   	}
>   
> +	/* ELS response from a target */
> +	if ((ntohs(oxid) >= FDLS_PLOGI_OXID_BASE)
> +		&& (ntohs(oxid) < FDLS_PRLI_OXID_BASE)) {
> +		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"Dropping Unknown frame in PLOGI exchange range type: 0x%x.",
> +				     fchdr->fh_type);
> +			return -1;
> +		}
> +		return FNIC_TPORT_PLOGI_RSP;
> +	}
> +	if ((ntohs(oxid) >= FDLS_PRLI_OXID_BASE)
> +		&& (ntohs(oxid) < FDLS_ADISC_OXID_BASE)) {
> +		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"Dropping Unknown frame in PRLI exchange range type: 0x%x.",
> +				     fchdr->fh_type);
> +			return -1;
> +		}
> +		return FNIC_TPORT_PRLI_RSP;
> +	}
> +
> +	if ((ntohs(oxid) >= FDLS_ADISC_OXID_BASE)
> +		&& (ntohs(oxid) < FDLS_TGT_OXID_POOL_END)) {
> +		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
> +			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +				"Dropping Unknown frame in ADISC exchange range type: 0x%x.",
> +				     fchdr->fh_type);
> +			return -1;
> +		}
> +		return FNIC_TPORT_ADISC_RSP;
> +	}
> +
>   	/*response from fabric */
>   	rsp_type = fnic_fdls_expected_rsp(iport, ntohs(oxid));
>   
> @@ -1885,6 +3290,21 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
>   	case FNIC_FABRIC_GPN_FT_RSP:
>   		fdls_process_gpn_ft_rsp(iport, fchdr, len);
>   		break;
> +	case FNIC_TPORT_PLOGI_RSP:
> +		fdls_process_tgt_plogi_rsp(iport, fchdr);
> +		break;
> +	case FNIC_TPORT_PRLI_RSP:
> +		fdls_process_tgt_prli_rsp(iport, fchdr);
> +		break;
> +	case FNIC_TPORT_ADISC_RSP:
> +		fdls_process_tgt_adisc_rsp(iport, fchdr);
> +		break;
> +	case FNIC_TPORT_LOGO_RSP:
> +		/* Logo response from tgt which we have deleted */
> +		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> +					 "Logo response from tgt: 0x%x",
> +			     ntoh24(fchdr->fh_s_id));
> +		break;
>   	case FNIC_FABRIC_LOGO_RSP:
>   		fdls_process_fabric_logo_rsp(iport, fchdr);
>   		break;
> @@ -1894,7 +3314,8 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
>   		if (fdls_is_oxid_in_fabric_range(oxid) &&
>   			(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
>   			fdls_process_fabric_abts_rsp(iport, fchdr);
> -		}
> +		} else
> +			fdls_process_tgt_abts_rsp(iport, fchdr);
>   		break;
>   	default:
>   		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
> diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
> index 2d5f438f2cc4..92cd17efa40f 100644
> --- a/drivers/scsi/fnic/fnic.h
> +++ b/drivers/scsi/fnic/fnic.h
> @@ -79,6 +79,9 @@
>   
>   #define IS_FNIC_FCP_INITIATOR(fnic) (fnic->role == FNIC_ROLE_FCP_INITIATOR)
>   
> +/* Retry supported by rport (returned by PRLI service parameters) */
> +#define FNIC_FC_RP_FLAGS_RETRY            0x1
> +
>   /*
>    * fnic private data per SCSI command.
>    * These fields are locked by the hashed io_req_lock.
> @@ -133,6 +136,7 @@ static inline u64 fnic_flags_and_state(struct scsi_cmnd *cmd)
>   
>   extern unsigned int fnic_log_level;
>   extern unsigned int io_completions;
> +extern struct workqueue_struct *fnic_event_queue;
>   
>   #define FNIC_MAIN_LOGGING 0x01
>   #define FNIC_FCS_LOGGING 0x02
> @@ -329,6 +333,8 @@ struct fnic {
>   	struct work_struct flush_work;
>   	struct sk_buff_head frame_queue;
>   	struct list_head tx_queue;
> +	struct work_struct tport_work;
> +	struct list_head tport_event_list;
>   
>   	/*** FIP related data members  -- start ***/
>   	void (*set_vlan)(struct fnic *, u16 vlan);
> diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
> index db0a6978504e..d2c3ebce3209 100644
> --- a/drivers/scsi/fnic/fnic_fdls.h
> +++ b/drivers/scsi/fnic/fnic_fdls.h
> @@ -385,7 +385,7 @@ int fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
>   								  void *rx_frame, int len,
>   								  int fchdr_offset);
>   void fdls_send_tport_abts(struct fnic_iport_s *iport,
> -			  struct fnic_tport_s *tport);
> +						struct fnic_tport_s *tport);
>   bool fdls_delete_tport(struct fnic_iport_s *iport,
>   		       struct fnic_tport_s *tport);
>   void fdls_fdmi_timer_callback(struct timer_list *t);

Cheers,

Hannes
diff mbox series

Patch

diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index 3a995b76a864..52459f6bb589 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -11,6 +11,10 @@ 
 #include <scsi/fc/fc_fcp.h>
 #include <linux/utsname.h>
 
+#define FC_FC4_TYPE_SCSI 0x08
+
+static void fdls_send_rpn_id(struct fnic_iport_s *iport);
+
 /* Frame initialization */
 /*
  * Variables:
@@ -66,6 +70,20 @@  struct fc_std_rpn_id fnic_std_rpn_id_req = {
 		      .ct_cmd = cpu_to_be16(FC_NS_RPN_ID)}
 };
 
+/*
+ * Variables:
+ * did, sid, oxid
+ */
+struct fc_std_els_prli fnic_std_prli_req = {
+	.fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
+		  .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}, .fh_rx_id = 0xFFFF},
+	.els_prli = {.prli_cmd = ELS_PRLI,
+		     .prli_spp_len = 16,
+		     .prli_len = cpu_to_be16(0x14)},
+	.sp = {.spp_type = 0x08, .spp_flags = 0x0020,
+	       .spp_params = cpu_to_be32(0xA2)}
+};
+
 /*
  * Variables:
  * fh_s_id, port_id, port_name
@@ -143,9 +161,19 @@  struct fc_frame_header fc_std_fabric_abts = {
 	.fh_parm_offset = 0x00000000,	/* bit:0 = 0 Abort a exchange */
 };
 
+struct fc_frame_header fc_std_tport_abts = {
+	.fh_r_ctl = FC_RCTL_BA_ABTS,	/* ABTS */
+	.fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS,
+	.fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00,
+	.fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000, .fh_rx_id = 0xFFFF,
+	.fh_parm_offset = 0x00000000,	/* bit:0 = 0 Abort a exchange */
+};
+
 #define RETRIES_EXHAUSTED(iport)      \
 	(iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)
 
+#define FNIC_TPORT_MAX_NEXUS_RESTART (8)
+
 /*
  * For fabric requests and fdmi, once OXIDs are allocated from the pool
  * (and a range) they are encoded with expected rsp type as
@@ -167,6 +195,14 @@  static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 				   void *rx_frame);
 static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
 static void fnic_fdls_start_flogi(struct fnic_iport_s *iport);
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+									  uint32_t fcid,
+									  uint64_t wwpn);
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport);
+static void fdls_start_tport_timer(struct fnic_iport_s *iport,
+					struct fnic_tport_s *tport, int timeout);
+static void fdls_tport_timer_callback(struct timer_list *t);
+
 static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
 			int timeout);
 static void
@@ -182,6 +218,8 @@  void fdls_init_oxid_pool(struct fnic_iport_s *iport)
 	fdls_init_fabric_oxid_pool(&iport->fdmi_oxid_pool,
 			FDLS_FDMI_OXID_POOL_BASE,
 			FDLS_FDMI_OXID_POOL_SZ);
+
+	fdls_init_tgt_oxid_pool(iport);
 }
 
 uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport,
@@ -313,6 +351,13 @@  static inline void fdls_schedule_fabric_oxid_free(struct fnic_iport_s
 			    iport->fabric_oxid_pool.active_oxid_fabric_req);
 }
 
+static inline void fdls_schedule_tgt_oxid_free(struct fnic_iport_s *iport,
+					       struct fnic_tgt_oxid_pool_s
+					       *oxid_pool, uint16_t oxid)
+{
+	fdls_schedule_oxid_free(&oxid_pool->meta, oxid);
+}
+
 int fnic_fdls_expected_rsp(struct fnic_iport_s *iport, uint16_t oxid)
 {
 	struct fnic *fnic = iport->fnic;
@@ -340,6 +385,62 @@  static int fdls_is_oxid_in_fabric_range(uint16_t oxid)
 			(oxid_unmasked <= FDLS_FABRIC_OXID_POOL_END));
 }
 
+void fdls_init_tgt_oxid_pool(struct fnic_iport_s *iport)
+{
+	memset(&iport->plogi_oxid_pool, 0, sizeof(iport->plogi_oxid_pool));
+	iport->plogi_oxid_pool.meta.oxid_base = FDLS_PLOGI_OXID_BASE;
+	iport->plogi_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
+	INIT_LIST_HEAD(&iport->plogi_oxid_pool.meta.reclaim_list);
+
+	memset(&iport->prli_oxid_pool, 0, sizeof(iport->prli_oxid_pool));
+	iport->prli_oxid_pool.meta.oxid_base = FDLS_PRLI_OXID_BASE;
+	iport->prli_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
+	INIT_LIST_HEAD(&iport->prli_oxid_pool.meta.reclaim_list);
+
+	memset(&iport->adisc_oxid_pool, 0, sizeof(iport->adisc_oxid_pool));
+	iport->adisc_oxid_pool.meta.oxid_base = FDLS_ADISC_OXID_BASE;
+	iport->adisc_oxid_pool.meta.sz = FDLS_TGT_OXID_BLOCK_SZ;
+	INIT_LIST_HEAD(&iport->adisc_oxid_pool.meta.reclaim_list);
+}
+
+inline uint16_t fdls_alloc_tgt_oxid(struct fnic_iport_s *iport,
+				    struct fnic_tgt_oxid_pool_s *oxid_pool)
+{
+	uint16_t oxid;
+
+	oxid = fdls_alloc_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap);
+	return oxid;
+}
+
+inline void fdls_free_tgt_oxid(struct fnic_iport_s *iport,
+			       struct fnic_tgt_oxid_pool_s *oxid_pool,
+			       uint16_t oxid)
+{
+	fdls_free_oxid(iport, &oxid_pool->meta, oxid_pool->bitmap, oxid);
+}
+
+static struct fnic_tgt_oxid_pool_s *fdls_get_tgt_oxid_pool(struct fnic_tport_s
+							   *tport)
+{
+	struct fnic_iport_s *iport = (struct fnic_iport_s *)tport->iport;
+	struct fnic_tgt_oxid_pool_s *oxid_pool = NULL;
+
+	switch (tport->state) {
+	case FDLS_TGT_STATE_PLOGI:
+		oxid_pool = &iport->plogi_oxid_pool;
+		break;
+	case FDLS_TGT_STATE_PRLI:
+		oxid_pool = &iport->prli_oxid_pool;
+		break;
+	case FDLS_TGT_STATE_ADISC:
+		oxid_pool = &iport->adisc_oxid_pool;
+		break;
+	default:
+		break;
+	}
+	return oxid_pool;
+}
+
 inline void fnic_del_fabric_timer_sync(struct fnic *fnic)
 {
 	fnic->iport.fabric.del_timer_inprogress = 1;
@@ -383,6 +484,56 @@  fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
 				 "fabric timer is %d ", timeout);
 }
 
+static void
+fdls_start_tport_timer(struct fnic_iport_s *iport,
+					   struct fnic_tport_s *tport, int timeout)
+{
+	u64 fabric_tov;
+	struct fnic *fnic = iport->fnic;
+
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport fcid 0x%x: Canceling disc timer\n",
+					 tport->fcid);
+		fnic_del_tport_timer_sync(fnic, tport);
+		tport->timer_pending = 0;
+	}
+
+	if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED))
+		tport->retry_counter++;
+
+	fabric_tov = jiffies + msecs_to_jiffies(timeout);
+	mod_timer(&tport->retry_timer, round_jiffies(fabric_tov));
+	tport->timer_pending = 1;
+}
+
+void
+fdls_send_tport_abts(struct fnic_iport_s *iport,
+					 struct fnic_tport_s *tport)
+{
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	struct fnic *fnic = iport->fnic;
+	struct fc_frame_header tport_abort = fc_std_tport_abts;
+	struct fc_frame_header *tport_abts = &tport_abort;
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+	FNIC_STD_SET_S_ID(tport_abts, s_id);
+	FNIC_STD_SET_D_ID(tport_abts, d_id);
+	tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
+
+	tport_abts->fh_ox_id = tport->oxid_used;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS sending tport abts: tport->state: %d ",
+				 tport->state);
+
+	fnic_send_fcoe_frame(iport, tport_abts, sizeof(struct fc_frame_header));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+
 static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
 {
 	uint8_t fcid[3];
@@ -614,6 +765,176 @@  static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
 	fdls_set_state((&iport->fabric), fdls_state);
 }
 
+static void
+fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fc_std_els_adisc adisc;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+
+	memset(&adisc, 0, sizeof(struct fc_std_els_adisc));
+	FNIC_STD_SET_R_CTL(&adisc.fchdr, 0x22);
+	FNIC_STD_SET_TYPE(&adisc.fchdr, 0x01);
+	FNIC_STD_SET_F_CTL(&adisc.fchdr, FNIC_ELS_REQ_FCTL << 16);
+	FNIC_STD_SET_RX_ID(&adisc.fchdr, cpu_to_be16(0xFFFF));
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+	FNIC_STD_SET_S_ID(&adisc.fchdr, s_id);
+	FNIC_STD_SET_D_ID(&adisc.fchdr, d_id);
+
+	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->adisc_oxid_pool));
+	if (oxid == 0xFFFF) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Failed to allocate OXID to send ADISC %p", iport);
+		return;
+	}
+
+	tport->oxid_used = oxid;
+	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+	FNIC_STD_SET_OX_ID((&adisc.fchdr), oxid);
+	FNIC_STD_SET_NPORT_NAME(&adisc.els.adisc_wwpn,
+				le64_to_cpu(iport->wwpn));
+	FNIC_STD_SET_NODE_NAME(&adisc.els.adisc_wwnn, le64_to_cpu(iport->wwnn));
+
+	memcpy(adisc.els.adisc_port_id, s_id, 3);
+	adisc.els.adisc_cmd = ELS_ADISC;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "sending ADISC to tgt fcid: 0x%x", tport->fcid);
+
+
+	fnic_send_fcoe_frame(iport, &adisc, sizeof(struct fc_std_els_adisc));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+
+bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fnic_tport_event_s *tport_del_evt;
+	struct fnic *fnic = iport->fnic;
+
+	if ((tport->state == FDLS_TGT_STATE_OFFLINING)
+	    || (tport->state == FDLS_TGT_STATE_OFFLINE)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			     "tport fcid 0x%x: tport state is offlining/offline\n",
+			     tport->fcid);
+		return false;
+	}
+
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
+	/*
+	 * By setting this flag, the tport will not be seen in a look-up
+	 * in an RSCN. Even if we move to multithreaded model, this tport
+	 * will be destroyed and a new RSCN will have to create a new one
+	 */
+	tport->flags |= FNIC_FDLS_TPORT_TERMINATING;
+
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport fcid 0x%x: Canceling disc timer\n",
+					 tport->fcid);
+		fnic_del_tport_timer_sync(fnic, tport);
+		tport->timer_pending = 0;
+	}
+
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		fnic_rport_exch_reset(iport->fnic, tport->fcid);
+		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+		if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) {
+			tport_del_evt =
+				kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+			if (!tport_del_evt) {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Failed to allocate memory for tport fcid: 0x%0x\n",
+					 tport->fcid);
+				return false;
+			}
+			tport_del_evt->event = TGT_EV_RPORT_DEL;
+			tport_del_evt->arg1 = (void *) tport;
+			list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+			queue_work(fnic_event_queue, &fnic->tport_work);
+		} else {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport 0x%x not reg with scsi_transport. Freeing locally",
+				 tport->fcid);
+			list_del(&tport->links);
+			kfree(tport);
+		}
+	}
+	return true;
+}
+
+static void
+fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fc_std_flogi plogi;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+	uint32_t timeout;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Send tgt PLOGI to fcid: 0x%x", tport->fcid);
+
+	memcpy(&plogi, &fnic_std_plogi_req, sizeof(struct fc_std_flogi));
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+
+	FNIC_STD_SET_S_ID(&plogi.fchdr, s_id);
+	FNIC_STD_SET_D_ID(&plogi.fchdr, d_id);
+	FNIC_LOGI_SET_RDF_SIZE(&plogi.els, iport->max_payload_size);
+
+	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool));
+	if (oxid == 0xFFFF) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
+				 iport->fcid, tport->fcid);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "send tgt PLOGI: tgt fcid: 0x%x oxid: 0x%x", tport->fcid,
+				 ntohs(oxid));
+	tport->oxid_used = oxid;
+	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+	FNIC_STD_SET_OX_ID((&plogi.fchdr), oxid);
+	FNIC_LOGI_SET_NPORT_NAME(&plogi.els, iport->wwpn);
+	FNIC_LOGI_SET_NODE_NAME(&plogi.els, iport->wwnn);
+
+	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+
+	fnic_send_fcoe_frame(iport, &plogi, sizeof(struct fc_std_flogi));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, timeout);
+}
+
+static uint16_t
+fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
+		      struct fc_std_flogi *plogi_rsp)
+{
+	uint16_t b2b_rdf_size =
+	    be16_to_cpu(FNIC_LOGI_RDF_SIZE(&plogi_rsp->els));
+	uint16_t spc3_rdf_size =
+	    be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & FNIC_FC_C3_RDF;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
+			 b2b_rdf_size, spc3_rdf_size);
+
+	return MIN(b2b_rdf_size, spc3_rdf_size);
+}
+
 static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
 {
 	struct fc_std_rft_id rft_id;
@@ -690,6 +1011,48 @@  static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
 	fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
 }
 
+static void
+fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fc_std_els_prli prli;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+	uint32_t timeout;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS sending PRLI to tgt: 0x%x", tport->fcid);
+
+	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->prli_oxid_pool));
+	if (oxid == 0xFFFF) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Failed to allocate OXID to send PRLI %p", iport);
+		return;
+	}
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS sending PRLI to tgt: 0x%x OXID: 0x%x", tport->fcid,
+				 ntohs(oxid));
+
+	tport->oxid_used = oxid;
+	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+	memcpy(&prli, &fnic_std_prli_req, sizeof(struct fc_std_els_prli));
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+
+	FNIC_STD_SET_S_ID((&prli.fchdr), s_id);
+	FNIC_STD_SET_D_ID((&prli.fchdr), d_id);
+	FNIC_STD_SET_OX_ID((&prli.fchdr), oxid);
+
+	timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+
+	fdls_get_tgt_oxid_pool(tport);
+	fnic_send_fcoe_frame(iport, &prli, sizeof(struct fc_std_els_prli));
+	/* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+	fdls_start_tport_timer(iport, tport, timeout);
+}
+
 /**
  * fdls_send_fabric_logo - Send flogo to the fcf
  * @iport: Handle to fnic iport
@@ -742,6 +1105,212 @@  void fdls_send_fabric_logo(struct fnic_iport_s *iport)
 	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo));
 }
 
+/**
+ * fdls_tgt_logout - Send plogo to the remote port
+ * @iport: Handle to fnic iport
+ * @tport: Handle to remote port
+ *
+ * This function does not change or check the fabric/tport state.
+ * It the caller's responsibility to set the appropriate tport/fabric
+ * state when this is called. Normally that is fdls_tgt_state_plogo.
+ * This could be used to send plogo to nameserver process
+ * also not just target processes
+ */
+void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fc_std_logo logo;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	struct fnic *fnic = iport->fnic;
+	uint16_t oxid;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Sending logo to tport fcid: 0x%x", tport->fcid);
+	memcpy(&logo, &fnic_std_logo_req, sizeof(struct fc_std_logo));
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+
+	FNIC_STD_SET_S_ID((&logo.fchdr), s_id);
+	FNIC_STD_SET_D_ID((&logo.fchdr), d_id);
+
+	oxid = htons(fdls_alloc_tgt_oxid(iport, &iport->plogi_oxid_pool));
+	FNIC_STD_SET_OX_ID((&logo.fchdr), oxid);
+
+	memcpy(&logo.els.fl_n_port_id, s_id, 3);
+	FNIC_STD_SET_NPORT_NAME(&logo.els.fl_n_port_wwn,
+				le64_to_cpu(iport->wwpn));
+
+
+	fnic_send_fcoe_frame(iport, &logo, sizeof(struct fc_std_logo));
+}
+
+static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
+{
+	struct fnic_tport_s *tport, *next;
+	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: Starting FDLS target discovery", iport->fcid);
+
+	list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+		if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+			|| (iport->state != FNIC_IPORT_STATE_READY)) {
+			break;
+		}
+		/* if we marked the tport as deleted due to GPN_FT
+		 * We should not send ADISC anymore
+		 */
+		if ((tport->state == FDLS_TGT_STATE_OFFLINING) ||
+			(tport->state == FDLS_TGT_STATE_OFFLINE))
+			continue;
+
+		/* For tports which have received RSCN */
+		if (tport->flags & FNIC_FDLS_TPORT_SEND_ADISC) {
+			tport->retry_counter = 0;
+			fdls_set_tport_state(tport, FDLS_TGT_STATE_ADISC);
+			tport->flags &= ~FNIC_FDLS_TPORT_SEND_ADISC;
+			fdls_send_tgt_adisc(iport, tport);
+			continue;
+		}
+		if (fdls_get_tport_state(tport) != FDLS_TGT_STATE_INIT) {
+			/* Not a new port, skip  */
+			continue;
+		}
+		tport->retry_counter = 0;
+		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+		fdls_send_tgt_plogi(iport, tport);
+	}
+	fdls_set_state((&iport->fabric), FDLS_STATE_TGT_DISCOVERY);
+}
+
+/*
+ * Function to restart the IT nexus if we received any out of
+ * sequence PLOGI/PRLI  response from the target.
+ * The memory for the new tport structure is allocated
+ * inside fdls_create_tport and added to the iport's tport list.
+ * This will get freed later during tport_offline/linkdown
+ * or module unload. The new_tport pointer will go out of scope
+ * safely since the memory it is
+ * pointing to it will be freed later
+ */
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
+{
+	struct fnic_iport_s *iport = tport->iport;
+	struct fnic_tport_s *new_tport = NULL;
+	uint32_t fcid;
+	uint64_t wwpn;
+	int nexus_restart_count;
+	struct fnic *fnic = iport->fnic;
+	bool retval = true;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport fcid: 0x%x state: %d restart_count: %d",
+				 tport->fcid, tport->state, tport->nexus_restart_count);
+
+	fcid = tport->fcid;
+	wwpn = tport->wwpn;
+	nexus_restart_count = tport->nexus_restart_count;
+
+	retval = fdls_delete_tport(iport, tport);
+	if (retval != true) {
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+			     "Error deleting tport: 0x%x", fcid);
+		return;
+	}
+
+	if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			     "Exceeded nexus restart retries tport: 0x%x",
+			     fcid);
+		return;
+	}
+
+	/*
+	 * Allocate memory for the new tport and add it to
+	 * iport's tport list.
+	 * This memory will be freed during tport_offline/linkdown
+	 * or module unload. The pointer new_tport is safe to go
+	 * out of scope when this function returns, since the memory
+	 * it is pointing to is guaranteed to be freed later
+	 * as mentioned above.
+	 */
+	new_tport = fdls_create_tport(iport, fcid, wwpn);
+	if (!new_tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Error creating new tport: 0x%x", fcid);
+		return;
+	}
+
+	new_tport->nexus_restart_count = nexus_restart_count + 1;
+	fdls_send_tgt_plogi(iport, new_tport);
+	fdls_set_tport_state(new_tport, FDLS_TGT_STATE_PLOGI);
+}
+
+struct fnic_tport_s *fnic_find_tport_by_fcid(struct fnic_iport_s *iport,
+									 uint32_t fcid)
+{
+	struct fnic_tport_s *tport, *next;
+
+	list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+		if ((tport->fcid == fcid)
+			&& !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+			return tport;
+	}
+	return NULL;
+}
+
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+								  uint32_t fcid, uint64_t wwpn)
+{
+	struct fnic_tport_s *tport;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);
+
+	tport = kzalloc(sizeof(struct fnic_tport_s), GFP_ATOMIC);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Memory allocation failure while creating tport: 0x%x\n",
+			 fcid);
+		return NULL;
+	}
+
+	tport->max_payload_size = FNIC_FCOE_MAX_FRAME_SZ;
+	tport->r_a_tov = FNIC_R_A_TOV_DEF;
+	tport->e_d_tov = FNIC_E_D_TOV_DEF;
+	tport->fcid = fcid;
+	tport->wwpn = wwpn;
+	tport->iport = iport;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Need to setup tport timer callback");
+
+	timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Added tport 0x%x", tport->fcid);
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
+	list_add_tail(&tport->links, &iport->tport_list);
+	atomic_set(&tport->in_flight, 0);
+	return tport;
+}
+
+struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
+									 uint64_t wwpn)
+{
+	struct fnic_tport_s *tport, *next;
+
+	list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+		if ((tport->wwpn == wwpn)
+			&& !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+			return tport;
+	}
+	return NULL;
+}
+
 void fdls_fabric_timer_callback(struct timer_list *t)
 {
 	struct fnic_fdls_fabric_s *fabric = from_timer(fabric, t, retry_timer);
@@ -847,90 +1416,588 @@  void fdls_fabric_timer_callback(struct timer_list *t)
 			/* ABTS has timed out */
 			fdls_schedule_fabric_oxid_free(iport);
 			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-						 "ABTS timed out. Starting PLOGI: %p", iport);
-			fnic_fdls_start_plogi(iport);
+						 "ABTS timed out. Starting PLOGI: %p", iport);
+			fnic_fdls_start_plogi(iport);
+		}
+		break;
+	case FDLS_STATE_REGISTER_FC4_TYPES:
+		/* scr received a LS_RJT with busy we retry from here */
+		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_register_fc4_types(iport);
+		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+			/* RFT_ID timed out send abts */
+			fdls_send_fabric_abts(iport);
+		} else {
+			/* ABTS has timed out */
+			fdls_schedule_fabric_oxid_free(iport);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"ABTS timed out. Starting PLOGI: %p", iport);
+			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
+		}
+		break;
+	case FDLS_STATE_REGISTER_FC4_FEATURES:
+		/* scr received a LS_RJT with busy we retry from here */
+		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_register_fc4_features(iport);
+		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+			/* SCR has timed out. Send abts */
+			fdls_send_fabric_abts(iport);
+		else {
+			/* ABTS has timed out */
+			fdls_schedule_fabric_oxid_free(iport);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"ABTS timed out. Starting PLOGI %p", iport);
+			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
+		}
+		break;
+	case FDLS_STATE_RSCN_GPN_FT:
+	case FDLS_STATE_SEND_GPNFT:
+	case FDLS_STATE_GPN_FT:
+		/* GPN_FT received a LS_RJT with busy we retry from here */
+		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_gpn_ft(iport, iport->fabric.state);
+		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+			/* gpn_ft has timed out. Send abts */
+			fdls_send_fabric_abts(iport);
+		} else {
+			/* ABTS has timed out */
+			fdls_schedule_fabric_oxid_free(iport);
+			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
+				fdls_send_gpn_ft(iport, iport->fabric.state);
+			} else {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
+					 iport);
+			}
+		}
+		break;
+	default:
+		fnic_fdls_start_flogi(iport);	/* Placeholder call */
+		break;
+	}
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
+{
+	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+	struct fnic *fnic = iport->fnic;
+	struct fnic_tport_event_s *tport_del_evt;
+
+	if (!IS_FNIC_FCP_INITIATOR(fnic))
+		return;
+
+	tport_del_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+	if (!tport_del_evt) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Failed to allocate memory for tport event fcid: 0x%x",
+			 tport->fcid);
+		return;
+	}
+	tport_del_evt->event = TGT_EV_TPORT_DELETE;
+	tport_del_evt->arg1 = (void *) tport;
+	list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+	queue_work(fnic_event_queue, &fnic->tport_work);
+}
+
+static void fdls_tport_timer_callback(struct timer_list *t)
+{
+	struct fnic_tport_s *tport = from_timer(tport, t, retry_timer);
+	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+	struct fnic *fnic = iport->fnic;
+	uint16_t oxid;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	if (!tport->timer_pending) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if (iport->state != FNIC_IPORT_STATE_READY) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if (tport->del_timer_inprogress) {
+		tport->del_timer_inprogress = 0;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n",
+			 tport->fcid);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		 "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d",
+		 tport->fcid, tport->timer_pending, tport->state,
+		 tport->retry_counter);
+
+	tport->timer_pending = 0;
+	oxid = ntohs(tport->oxid_used);
+
+	/* We retry plogi/prli/adisc frames depending on the tport state */
+	switch (tport->state) {
+	case FDLS_TGT_STATE_PLOGI:
+		/* PLOGI frame received a LS_RJT with busy, we retry from here */
+		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+			&& (tport->retry_counter < iport->max_plogi_retries)) {
+			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_tgt_plogi(iport, tport);
+		} else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+			/* Plogi frame has timed out, send abts */
+			fdls_send_tport_abts(iport, tport);
+		} else if (tport->retry_counter < iport->max_plogi_retries) {
+			/*
+			 * ABTS has timed out
+			 */
+			fdls_schedule_tgt_oxid_free(iport,
+						    &iport->plogi_oxid_pool,
+						    oxid);
+			fdls_send_tgt_plogi(iport, tport);
+		} else {
+			/* exceeded plogi retry count */
+			fdls_schedule_tgt_oxid_free(iport,
+						    &iport->plogi_oxid_pool,
+						    oxid);
+			fdls_send_delete_tport_msg(tport);
+		}
+		break;
+	case FDLS_TGT_STATE_PRLI:
+		/* PRLI received a LS_RJT with busy , hence we retry from here */
+		if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
+			tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+			fdls_send_tgt_prli(iport, tport);
+		} else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+			/* PRLI has time out, send abts */
+			fdls_send_tport_abts(iport, tport);
+		} else {
+			/* ABTS has timed out for prli, we go back to PLOGI */
+			fdls_schedule_tgt_oxid_free(iport,
+						    &iport->prli_oxid_pool,
+						    oxid);
+			fdls_send_tgt_plogi(iport, tport);
+			fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+		}
+		break;
+	case FDLS_TGT_STATE_ADISC:
+		/* ADISC timed out send an ABTS */
+		if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+			fdls_send_tport_abts(iport, tport);
+		} else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)
+				   && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+			/*
+			 * ABTS has timed out
+			 */
+			fdls_schedule_tgt_oxid_free(iport,
+						    &iport->adisc_oxid_pool,
+						    oxid);
+			fdls_send_tgt_adisc(iport, tport);
+		} else {
+			/* exceeded retry count */
+			fdls_schedule_tgt_oxid_free(iport,
+						    &iport->adisc_oxid_pool,
+						    oxid);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "ADISC not responding. Deleting target port: 0x%x",
+					 tport->fcid);
+			fdls_send_delete_tport_msg(tport);
+		}
+		break;
+	default:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Unknown tport state: 0x%x", tport->state);
+		break;
+	}
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
+{
+	iport->fabric.retry_counter = 0;
+	fdls_send_fabric_flogi(iport);
+	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
+	iport->fabric.flags = 0;
+}
+
+static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
+{
+	iport->fabric.retry_counter = 0;
+	fdls_send_fabric_plogi(iport);
+	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
+	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+}
+
+static void
+fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
+			   struct fc_frame_header *fchdr)
+{
+	uint32_t tgt_fcid;
+	struct fnic_tport_s *tport;
+	uint8_t *fcid;
+	uint64_t frame_wwnn;
+	uint64_t frame_wwpn;
+	uint16_t oxid;
+	struct fc_std_els_adisc *adisc_rsp = (struct fc_std_els_adisc *)fchdr;
+	struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
+	struct fnic *fnic = iport->fnic;
+
+	fcid = FNIC_STD_GET_S_ID(fchdr);
+	tgt_fcid = ntoh24(fcid);
+	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Tgt ADISC response tport not found: 0x%x", tgt_fcid);
+		return;
+	}
+	if ((iport->state != FNIC_IPORT_STATE_READY)
+		|| (tport->state != FDLS_TGT_STATE_ADISC)
+		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Dropping this ADISC response");
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "iport state: %d tport state: %d Is abort issued on PRLI? %d",
+			 iport->state, tport->state,
+			 (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED));
+		return;
+	}
+	if (ntohs(fchdr->fh_ox_id) != ntohs(tport->oxid_used)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Dropping frame from target: 0x%x",
+			 tgt_fcid);
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery");
+		return;
+	}
+
+	oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
+	fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, oxid);
+
+	switch (adisc_rsp->els.adisc_cmd) {
+	case ELS_LS_ACC:
+		if (tport->timer_pending) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "tport 0x%p Canceling fabric disc timer\n",
+						 tport);
+			fnic_del_tport_timer_sync(fnic, tport);
+		}
+		tport->timer_pending = 0;
+		tport->retry_counter = 0;
+		frame_wwnn = get_unaligned_be64(&adisc_rsp->els.adisc_wwnn);
+		frame_wwpn = get_unaligned_be64(&adisc_rsp->els.adisc_wwpn);
+		if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "ADISC accepted from target: 0x%x. Target logged in",
+				 tgt_fcid);
+			fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+		} else {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "Error mismatch frame: ADISC");
+		}
+		break;
+
+	case ELS_LS_RJT:
+		if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
+		     || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
+			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "ADISC ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
+				 tgt_fcid);
+
+			/* Retry ADISC again from the timer routine. */
+			tport->flags |= FNIC_FDLS_RETRY_FRAME;
+		} else {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "ADISC returned ELS_LS_RJT from target: 0x%x",
+						 tgt_fcid);
+			fdls_delete_tport(iport, tport);
+		}
+		break;
+	}
+}
+
+
+static void
+fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
+			   struct fc_frame_header *fchdr)
+{
+	uint32_t tgt_fcid;
+	struct fnic_tport_s *tport;
+	uint8_t *fcid;
+	uint16_t oxid;
+	struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *)fchdr;
+	struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
+	int max_payload_size;
+	struct fnic *fnic = iport->fnic;
+
+	fcid = FNIC_STD_GET_S_ID(fchdr);
+	tgt_fcid = ntoh24(fcid);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS processing target PLOGI response: tgt_fcid: 0x%x",
+				 tgt_fcid);
+
+	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport not found: 0x%x", tgt_fcid);
+		return;
+	}
+	if ((iport->state != FNIC_IPORT_STATE_READY)
+		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Dropping frame! iport state: %d tport state: %d",
+					 iport->state, tport->state);
+		return;
+	}
+
+	if (tport->state != FDLS_TGT_STATE_PLOGI) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			     "PLOGI rsp recvd in wrong state. Drop the frame and restart nexus");
+		fdls_target_restart_nexus(tport);
+		return;
+	}
+
+	if (fchdr->fh_ox_id != tport->oxid_used) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "PLOGI response from target: 0x%x. Dropping frame",
+			 tgt_fcid);
+		return;
+	}
+
+	oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
+	fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, oxid);
+
+	switch (plogi_rsp->els.fl_cmd) {
+	case ELS_LS_ACC:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PLOGI accepted by target: 0x%x", tgt_fcid);
+		break;
+
+	case ELS_LS_RJT:
+		if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
+		     || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
+			&& (tport->retry_counter < iport->max_plogi_retries)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "PLOGI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
+				 tgt_fcid);
+			/* Retry plogi again from the timer routine. */
+			tport->flags |= FNIC_FDLS_RETRY_FRAME;
+			return;
+		}
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PLOGI returned ELS_LS_RJT from target: 0x%x",
+					 tgt_fcid);
+		fdls_delete_tport(iport, tport);
+		return;
+
+	default:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PLOGI not accepted from target fcid: 0x%x",
+					 tgt_fcid);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Found the PLOGI target: 0x%x and state: %d",
+				 (unsigned int) tgt_fcid, tport->state);
+
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport fcid 0x%x: Canceling disc timer\n",
+					 tport->fcid);
+		fnic_del_tport_timer_sync(fnic, tport);
+	}
+
+	tport->timer_pending = 0;
+	tport->wwpn = get_unaligned_be64(&FNIC_LOGI_PORT_NAME(&plogi_rsp->els));
+	tport->wwnn = get_unaligned_be64(&FNIC_LOGI_NODE_NAME(&plogi_rsp->els));
+
+	/* Learn the Service Params */
+
+	/* Max frame size - choose the lowest */
+	max_payload_size = fnic_fc_plogi_rsp_rdf(iport, plogi_rsp);
+	tport->max_payload_size =
+		MIN(max_payload_size, iport->max_payload_size);
+
+	if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "MFS: tport max frame size below spec bounds: %d",
+			 tport->max_payload_size);
+		tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		 "MAX frame size: %d iport max_payload_size: %d tport mfs: %d",
+		 max_payload_size, iport->max_payload_size,
+		 tport->max_payload_size);
+
+	tport->max_concur_seqs = FNIC_FC_PLOGI_RSP_CONCUR_SEQ(plogi_rsp);
+
+	tport->retry_counter = 0;
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_PRLI);
+	fdls_send_tgt_prli(iport, tport);
+}
+
+static void
+fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
+			  struct fc_frame_header *fchdr)
+{
+	uint32_t tgt_fcid;
+	struct fnic_tport_s *tport;
+	uint8_t *fcid;
+	uint16_t oxid;
+	struct fc_std_els_prli *prli_rsp = (struct fc_std_els_prli *)fchdr;
+	struct fc_std_els_rsp *els_rjt = (struct fc_std_els_rsp *)fchdr;
+	struct fnic_tport_event_s *tport_add_evt;
+	struct fnic *fnic = iport->fnic;
+	bool mismatched_tgt = false;
+
+	fcid = FNIC_STD_GET_S_ID(fchdr);
+	tgt_fcid = ntoh24(fcid);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS process tgt PRLI response: 0x%x", tgt_fcid);
+
+	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport not found: 0x%x", tgt_fcid);
+		/* Handle or just drop? */
+		return;
+	}
+
+	if ((iport->state != FNIC_IPORT_STATE_READY)
+		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x",
+			 iport->state, tport->state, tport->fcid);
+		return;
+	}
+
+	if (tport->state != FDLS_TGT_STATE_PRLI) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			     "PRLI rsp recvd in wrong state. Drop frame. Restarting nexus");
+		fdls_target_restart_nexus(tport);
+		return;
+	}
+
+	if (fchdr->fh_ox_id != tport->oxid_used) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Dropping PRLI response from target: 0x%x ",
+			 tgt_fcid);
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery");
+		return;
+	}
+
+	oxid = ntohs(FNIC_STD_GET_OX_ID(fchdr));
+	fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
+
+	switch (prli_rsp->els_prli.prli_cmd) {
+	case ELS_LS_ACC:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PRLI accepted from target: 0x%x", tgt_fcid);
+
+		if (prli_rsp->sp.spp_type != FC_FC4_TYPE_SCSI) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "mismatched target zoned with FC SCSI initiator: 0x%x",
+				 tgt_fcid);
+			mismatched_tgt = true;
 		}
-		break;
-	case FDLS_STATE_REGISTER_FC4_TYPES:
-		/* scr received a LS_RJT with busy we retry from here */
-		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
-			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
-			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
-			fdls_send_register_fc4_types(iport);
-		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
-			/* RFT_ID timed out send abts */
-			fdls_send_fabric_abts(iport);
-		} else {
-			/* ABTS has timed out */
-			fdls_schedule_fabric_oxid_free(iport);
-			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-						 "ABTS timed out. Starting PLOGI: %p", iport);
-			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
+		if (mismatched_tgt) {
+			fdls_tgt_logout(iport, tport);
+			fdls_delete_tport(iport, tport);
+			return;
 		}
 		break;
-	case FDLS_STATE_REGISTER_FC4_FEATURES:
-		/* scr received a LS_RJT with busy we retry from here */
-		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
-			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
-			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
-			fdls_send_register_fc4_features(iport);
-		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
-			/* SCR has timed out. Send abts */
-			fdls_send_fabric_abts(iport);
-		else {
-			/* ABTS has timed out */
-			fdls_schedule_fabric_oxid_free(iport);
+	case ELS_LS_RJT:
+		if (((els_rjt->u.rej.er_reason == ELS_RJT_BUSY)
+		     || (els_rjt->u.rej.er_reason == ELS_RJT_UNAB))
+			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
+
 			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-						 "ABTS timed out. Starting PLOGI %p", iport);
-			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
-		}
-		break;
-	case FDLS_STATE_RSCN_GPN_FT:
-	case FDLS_STATE_SEND_GPNFT:
-	case FDLS_STATE_GPN_FT:
-		/* GPN_FT received a LS_RJT with busy we retry from here */
-		if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
-			&& (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
-			iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
-			fdls_send_gpn_ft(iport, iport->fabric.state);
-		} else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
-			/* gpn_ft has timed out. Send abts */
-			fdls_send_fabric_abts(iport);
+				 "PRLI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
+				 tgt_fcid);
+
+			/*Retry Plogi again from the timer routine. */
+			tport->flags |= FNIC_FDLS_RETRY_FRAME;
+			return;
 		} else {
-			/* ABTS has timed out */
-			fdls_schedule_fabric_oxid_free(iport);
-			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
-				fdls_send_gpn_ft(iport, iport->fabric.state);
-			} else {
-				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
-					 iport);
-			}
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "PRLI returned ELS_LS_RJT from target: 0x%x",
+						 tgt_fcid);
+
+			fdls_tgt_logout(iport, tport);
+			fdls_delete_tport(iport, tport);
+			return;
 		}
 		break;
+
 	default:
-		fnic_fdls_start_flogi(iport);	/* Placeholder call */
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "PRLI not accepted from target: 0x%x", tgt_fcid);
+		return;
 		break;
 	}
-	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-}
 
-static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
-{
-	iport->fabric.retry_counter = 0;
-	fdls_send_fabric_flogi(iport);
-	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
-	iport->fabric.flags = 0;
-}
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Found the PRLI target: 0x%x and state: %d",
+				 (unsigned int) tgt_fcid, tport->state);
 
-static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
-{
-	iport->fabric.retry_counter = 0;
-	fdls_send_fabric_plogi(iport);
-	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
-	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "tport fcid 0x%x: Canceling disc timer\n",
+					 tport->fcid);
+		fnic_del_tport_timer_sync(fnic, tport);
+	}
+	tport->timer_pending = 0;
+
+	/* Learn Service Params */
+	tport->fcp_csp = be32_to_cpu(prli_rsp->sp.spp_params);
+	tport->retry_counter = 0;
+
+	if (prli_rsp->sp.spp_params & FCP_SPPF_RETRY)
+		tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
+
+	/* Check if the device plays Target Mode Function */
+	if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "Remote port(0x%x): no target support. Deleting it\n",
+			 tgt_fcid);
+		fdls_tgt_logout(iport, tport);
+		fdls_delete_tport(iport, tport);
+		return;
+	}
+
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+
+	/* Inform the driver about new target added */
+	tport_add_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+	if (!tport_add_evt) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport event memory allocation failure: 0x%0x\n",
+				 tport->fcid);
+		return;
+	}
+	tport_add_evt->event = TGT_EV_RPORT_ADD;
+	tport_add_evt->arg1 = (void *) tport;
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "iport fcid: 0x%x add tport event fcid: 0x%x\n",
+			 tport->fcid, iport->fcid);
+	list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
+	queue_work(fnic_event_queue, &fnic->tport_work);
 }
 
+
 static void
 fdls_process_rff_id_rsp(struct fnic_iport_s *iport,
 			struct fc_frame_header *fchdr)
@@ -1179,7 +2246,8 @@  fdls_process_scr_rsp(struct fnic_iport_s *iport,
 						 iport);
 			if (iport->fabric.timer_pending) {
 				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
-							 "Canceling fabric disc timer %p\n", iport);
+					     "Canceling fabric disc timer %p\n",
+					     iport);
 				fnic_del_fabric_timer_sync(fnic);
 			}
 			fdls->timer_pending = 0;
@@ -1192,6 +2260,104 @@  fdls_process_scr_rsp(struct fnic_iport_s *iport,
 	}
 }
 
+static void
+fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
+			     struct fc_frame_header *fchdr, int len)
+{
+	struct fc_gpn_ft_rsp_iu *gpn_ft_tgt;
+	struct fnic_tport_s *tport, *next;
+	uint32_t fcid;
+	uint64_t wwpn;
+	int rem_len = len;
+	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+	struct fnic *fnic = iport->fnic;
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
+
+	gpn_ft_tgt =
+	    (struct fc_gpn_ft_rsp_iu *)((uint8_t *) fchdr +
+					sizeof(struct fc_frame_header)
+					+ sizeof(struct fc_ct_hdr));
+	len -= sizeof(struct fc_frame_header) + sizeof(struct fc_ct_hdr);
+
+	while (rem_len > 0) {
+
+		fcid = ntoh24(gpn_ft_tgt->fcid);
+		wwpn = ntohll(gpn_ft_tgt->wwpn);
+
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
+
+		if (fcid == iport->fcid) {
+			if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
+				break;
+			gpn_ft_tgt++;
+			rem_len -= sizeof(struct fc_gpn_ft_rsp_iu);
+			continue;
+		}
+
+		tport = fnic_find_tport_by_wwpn(iport, wwpn);
+		if (!tport) {
+			/*
+			 * New port registered with the switch or first time query
+			 */
+			tport = fdls_create_tport(iport, fcid, wwpn);
+			if (!tport)
+				return;
+		}
+		/*
+		 * check if this was an existing tport with same fcid
+		 * but whose wwpn has changed now ,then remove it and
+		 * create a new one
+		 */
+		if (tport->fcid != fcid) {
+			fdls_delete_tport(iport, tport);
+			tport = fdls_create_tport(iport, fcid, wwpn);
+			if (!tport)
+				return;
+		}
+
+		/*
+		 * If this GPN_FT rsp is after RSCN then mark the tports which
+		 * matches with the new GPN_FT list, if some tport is not
+		 * found in GPN_FT we went to delete that tport later.
+		 */
+		if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT)
+			tport->flags |= FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+
+		if (gpn_ft_tgt->ctrl & FNIC_FC_GPN_LAST_ENTRY)
+			break;
+
+		gpn_ft_tgt++;
+		rem_len -= sizeof(struct fc_gpn_ft_rsp_iu);
+	}
+	if (rem_len <= 0) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			 "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
+			 len, rem_len);
+	}
+
+	/*remove those ports which was not listed in GPN_FT */
+	if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT) {
+		list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+
+			if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Remove port: 0x%x not found in GPN_FT list",
+					 tport->fcid);
+				fdls_delete_tport(iport, tport);
+			} else {
+				tport->flags &= ~FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+			}
+			if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+				|| (iport->state != FNIC_IPORT_STATE_READY)) {
+				return;
+			}
+		}
+	}
+}
+
 static void
 fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 			struct fc_frame_header *fchdr, int len)
@@ -1200,6 +2366,9 @@  fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 	struct fc_std_gpn_ft *gpn_ft_rsp = (struct fc_std_gpn_ft *) fchdr;
 	uint16_t rsp;
 	uint8_t reason_code;
+	int count = 0;
+	struct fnic_tport_s *tport, *next;
+	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
 	struct fnic *fnic = iport->fnic;
 
 	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
@@ -1239,12 +2408,74 @@  fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 	case FC_FS_ACC:
 		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
 					 "0x%x: GPNFT_RSP accept", iport->fcid);
+		if (iport->fabric.timer_pending) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "0x%x: Canceling fabric disc timer\n",
+						 iport->fcid);
+			fnic_del_fabric_timer_sync(fnic);
+		}
+		iport->fabric.timer_pending = 0;
+		iport->fabric.retry_counter = 0;
+		fdls_process_gpn_ft_tgt_list(iport, fchdr, len);
+
+		/*
+		 * iport state can change only if link down event happened
+		 * We don't need to undo fdls_process_gpn_ft_tgt_list,
+		 * that will be taken care in next link up event
+		 */
+		if (iport->state != FNIC_IPORT_STATE_READY) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Halting target discovery: fab st: %d iport st: %d ",
+				 fdls_get_state(fdls), iport->state);
+			break;
+		}
+		fdls_tgt_discovery_start(iport);
 		break;
 
 	case FC_FS_RJT:
 		reason_code = gpn_ft_rsp->fc_std_ct_hdr.ct_reason;
 		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
 			 "0x%x: GPNFT_RSP Reject reason: %d", iport->fcid, reason_code);
+
+		if (((reason_code == FC_FS_RJT_BSY)
+		     || (reason_code == FC_FS_RJT_UNABL))
+			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine",
+				 iport->fcid);
+			/* Retry again from the timer routine */
+			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+		} else {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "0x%x: GPNFT_RSP reject", iport->fcid);
+			if (iport->fabric.timer_pending) {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+							 "0x%x: Canceling fabric disc timer\n",
+							 iport->fcid);
+				fnic_del_fabric_timer_sync(fnic);
+			}
+			iport->fabric.timer_pending = 0;
+			iport->fabric.retry_counter = 0;
+			/*
+			 * If GPN_FT ls_rjt then we should delete
+			 * all existing tports
+			 */
+			count = 0;
+			list_for_each_entry_safe(tport, next, &iport->tport_list,
+									 links) {
+				FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+							 "GPN_FT_REJECT: Remove port: 0x%x",
+							 tport->fcid);
+				fdls_delete_tport(iport, tport);
+				if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+					|| (iport->state != FNIC_IPORT_STATE_READY)) {
+					return;
+				}
+				count++;
+			}
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+						 "GPN_FT_REJECT: Removed (0x%x) ports", count);
+		}
 		break;
 
 	default:
@@ -1494,8 +2725,7 @@  fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 			     struct fc_frame_header *fchdr)
 {
 	uint32_t s_id;
-	struct fc_std_abts_ba_acc *ba_acc =
-	(struct fc_std_abts_ba_acc *) fchdr;
+	struct fc_std_abts_ba_acc *ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
 	struct fc_std_abts_ba_rjt *ba_rjt;
 	uint32_t fabric_state = iport->fabric.state;
 	struct fnic *fnic = iport->fnic;
@@ -1660,6 +2890,148 @@  fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 	}
 }
 
+static void
+fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
+			  struct fc_frame_header *fchdr)
+{
+	uint32_t s_id;
+	struct fnic_tport_s *tport;
+	uint32_t tport_state;
+	struct fc_std_abts_ba_acc *ba_acc;
+	struct fc_std_abts_ba_rjt *ba_rjt;
+	uint16_t oxid;
+	struct fnic *fnic = iport->fnic;
+
+	s_id = ntoh24(fchdr->fh_s_id);
+	ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
+	ba_rjt = (struct fc_std_abts_ba_rjt *)fchdr;
+
+	tport = fnic_find_tport_by_fcid(iport, s_id);
+	if (!tport) {
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+					 "Received tgt abts rsp with invalid SID: 0x%x", s_id);
+		return;
+	}
+
+	if (tport->timer_pending) {
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+					 "tport 0x%p Canceling fabric disc timer\n", tport);
+		fnic_del_tport_timer_sync(fnic, tport);
+	}
+	if (iport->state != FNIC_IPORT_STATE_READY) {
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+					 "Received tgt abts rsp in iport state(%d). Dropping.",
+					 iport->state);
+		return;
+	}
+	tport->timer_pending = 0;
+	tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+	tport_state = tport->state;
+	oxid = ntohs(fchdr->fh_ox_id);
+
+	/*This abort rsp is for ADISC */
+	if ((oxid >= FDLS_ADISC_OXID_BASE) && (oxid < FDLS_TGT_OXID_POOL_END)) {
+		if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) {
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+				     "OX_ID: 0x%x tgt_fcid: 0x%x rcvd tgt adisc abts resp BA_ACC",
+				     be16_to_cpu(ba_acc->acc.ba_ox_id),
+				     tport->fcid);
+		} else if (fchdr->fh_r_ctl == FNIC_BA_RJT_RCTL) {
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+				 "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ",
+				 tport->fcid, tport_state);
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+				 "reason code: 0x%x reason code explanation:0x%x ",
+				     ba_rjt->rjt.br_reason,
+				     ba_rjt->rjt.br_explan);
+		}
+		if ((tport->retry_counter < FDLS_RETRY_COUNT)
+		    && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
+			fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool,
+					   oxid);
+			fdls_send_tgt_adisc(iport, tport);
+			return;
+		}
+
+		fdls_free_tgt_oxid(iport, &iport->adisc_oxid_pool, oxid);
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+					 "ADISC not responding. Deleting target port: 0x%x",
+					 tport->fcid);
+		fdls_delete_tport(iport, tport);
+		if ((iport->state == FNIC_IPORT_STATE_READY)
+			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+		}
+		/*Restart a discovery of targets */
+		return;
+	}
+
+	/*This abort rsp is for PLOGI */
+	if ((oxid >= FDLS_PLOGI_OXID_BASE) && (oxid < FDLS_PRLI_OXID_BASE)) {
+		if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) {
+			FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+				 "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x",
+				 tport->fcid);
+		} else if (fchdr->fh_r_ctl == FNIC_BA_RJT_RCTL) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x",
+				     tport->fcid, fchdr->fh_ox_id);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "reason code: 0x%x reason code explanation: 0x%x",
+				     ba_rjt->rjt.br_reason,
+				     ba_rjt->rjt.br_explan);
+		}
+		if ((tport->retry_counter < iport->max_plogi_retries)
+		    && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
+			fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool,
+					   oxid);
+			fdls_send_tgt_plogi(iport, tport);
+			return;
+		}
+
+		fdls_free_tgt_oxid(iport, &iport->plogi_oxid_pool, oxid);
+		fdls_delete_tport(iport, tport);
+		/*Restart a discovery of targets */
+		if ((iport->state == FNIC_IPORT_STATE_READY)
+			&& (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+			&& (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+		}
+		return;
+	}
+
+	/*This abort rsp is for PRLI */
+	if ((oxid >= FDLS_PRLI_OXID_BASE) && (oxid < FDLS_ADISC_OXID_BASE)) {
+		if (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "0x%x: Received tgt PRLI abts response BA_ACC",
+				 tport->fcid);
+		} else if (fchdr->fh_r_ctl == FNIC_BA_RJT_RCTL) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ",
+				     tport->fcid, fchdr->fh_ox_id);
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "reason code: 0x%x reason code explanation: 0x%x",
+				     ba_rjt->rjt.br_reason,
+				     ba_rjt->rjt.br_explan);
+		}
+		if ((tport->retry_counter < FDLS_RETRY_COUNT)
+		    && (fchdr->fh_r_ctl == FNIC_BA_ACC_RCTL)) {
+			fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
+			fdls_send_tgt_prli(iport, tport);
+			return;
+		}
+		fdls_free_tgt_oxid(iport, &iport->prli_oxid_pool, oxid);
+		fdls_send_tgt_plogi(iport, tport);	/* go back to plogi */
+		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+		return;
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Received ABTS response for unknown frame %p", iport);
+}
+
 /*
  * Performs a validation for all FCOE frames and return the frame type
  */
@@ -1757,6 +3129,39 @@  fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 		}
 	}
 
+	/* ELS response from a target */
+	if ((ntohs(oxid) >= FDLS_PLOGI_OXID_BASE)
+		&& (ntohs(oxid) < FDLS_PRLI_OXID_BASE)) {
+		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"Dropping Unknown frame in PLOGI exchange range type: 0x%x.",
+				     fchdr->fh_type);
+			return -1;
+		}
+		return FNIC_TPORT_PLOGI_RSP;
+	}
+	if ((ntohs(oxid) >= FDLS_PRLI_OXID_BASE)
+		&& (ntohs(oxid) < FDLS_ADISC_OXID_BASE)) {
+		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"Dropping Unknown frame in PRLI exchange range type: 0x%x.",
+				     fchdr->fh_type);
+			return -1;
+		}
+		return FNIC_TPORT_PRLI_RSP;
+	}
+
+	if ((ntohs(oxid) >= FDLS_ADISC_OXID_BASE)
+		&& (ntohs(oxid) < FDLS_TGT_OXID_POOL_END)) {
+		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				"Dropping Unknown frame in ADISC exchange range type: 0x%x.",
+				     fchdr->fh_type);
+			return -1;
+		}
+		return FNIC_TPORT_ADISC_RSP;
+	}
+
 	/*response from fabric */
 	rsp_type = fnic_fdls_expected_rsp(iport, ntohs(oxid));
 
@@ -1885,6 +3290,21 @@  void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 	case FNIC_FABRIC_GPN_FT_RSP:
 		fdls_process_gpn_ft_rsp(iport, fchdr, len);
 		break;
+	case FNIC_TPORT_PLOGI_RSP:
+		fdls_process_tgt_plogi_rsp(iport, fchdr);
+		break;
+	case FNIC_TPORT_PRLI_RSP:
+		fdls_process_tgt_prli_rsp(iport, fchdr);
+		break;
+	case FNIC_TPORT_ADISC_RSP:
+		fdls_process_tgt_adisc_rsp(iport, fchdr);
+		break;
+	case FNIC_TPORT_LOGO_RSP:
+		/* Logo response from tgt which we have deleted */
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "Logo response from tgt: 0x%x",
+			     ntoh24(fchdr->fh_s_id));
+		break;
 	case FNIC_FABRIC_LOGO_RSP:
 		fdls_process_fabric_logo_rsp(iport, fchdr);
 		break;
@@ -1894,7 +3314,8 @@  void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 		if (fdls_is_oxid_in_fabric_range(oxid) &&
 			(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
 			fdls_process_fabric_abts_rsp(iport, fchdr);
-		}
+		} else
+			fdls_process_tgt_abts_rsp(iport, fchdr);
 		break;
 	default:
 		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 2d5f438f2cc4..92cd17efa40f 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -79,6 +79,9 @@ 
 
 #define IS_FNIC_FCP_INITIATOR(fnic) (fnic->role == FNIC_ROLE_FCP_INITIATOR)
 
+/* Retry supported by rport (returned by PRLI service parameters) */
+#define FNIC_FC_RP_FLAGS_RETRY            0x1
+
 /*
  * fnic private data per SCSI command.
  * These fields are locked by the hashed io_req_lock.
@@ -133,6 +136,7 @@  static inline u64 fnic_flags_and_state(struct scsi_cmnd *cmd)
 
 extern unsigned int fnic_log_level;
 extern unsigned int io_completions;
+extern struct workqueue_struct *fnic_event_queue;
 
 #define FNIC_MAIN_LOGGING 0x01
 #define FNIC_FCS_LOGGING 0x02
@@ -329,6 +333,8 @@  struct fnic {
 	struct work_struct flush_work;
 	struct sk_buff_head frame_queue;
 	struct list_head tx_queue;
+	struct work_struct tport_work;
+	struct list_head tport_event_list;
 
 	/*** FIP related data members  -- start ***/
 	void (*set_vlan)(struct fnic *, u16 vlan);
diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
index db0a6978504e..d2c3ebce3209 100644
--- a/drivers/scsi/fnic/fnic_fdls.h
+++ b/drivers/scsi/fnic/fnic_fdls.h
@@ -385,7 +385,7 @@  int fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 								  void *rx_frame, int len,
 								  int fchdr_offset);
 void fdls_send_tport_abts(struct fnic_iport_s *iport,
-			  struct fnic_tport_s *tport);
+						struct fnic_tport_s *tport);
 bool fdls_delete_tport(struct fnic_iport_s *iport,
 		       struct fnic_tport_s *tport);
 void fdls_fdmi_timer_callback(struct timer_list *t);