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
State New
Headers show
Series Introduce support for Fabric Discovery and... | expand

Commit Message

Karan Tilak Kumar 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
Karan Tilak Kumar Oct. 29, 2024, 12:37 a.m. UTC | #2
On Thursday, October 24, 2024 12:28 AM, Hannes Reinecke <hare@suse.de> wrote:
> 
> 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.

Thanks Hannes. Sure, I'll do this in the next version.

> > +
> > +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?

This is a byte-to-byte copy after hton24 converts the value to big endian.
Please let us know your thoughts here.

> > +	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.

Thanks Hannes. Sure, I'll fix this in the next version.

> 
> > +
> > +	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.

Thanks Hannes. Sure, I'll fix this in the next version.

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

No, there cannot be two entries with the same fcid in the list.
Please let us know your thoughts here.

> > +	}
> > +	return NULL;
> > +}
> > +
> > +
> > +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 ...

No, there cannot be two entries with the same wwpns in the list.
Please let us know your thoughts here.

> > +	}
> > +	return NULL;
> > +}

Regards,
Karan
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);