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