[SCSI] lpfc 8.2.8 : Miscellaneous Discovery Fixes
authorJames Smart <James.Smart@Emulex.Com>
Mon, 25 Aug 2008 01:49:45 +0000 (21:49 -0400)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Mon, 13 Oct 2008 13:28:53 +0000 (09:28 -0400)
Miscellaneous Discovery fixes:
- Fix rejection followed by acceptance in handling RPL and RPS
  unsolicited events
- Fix for vport delete crash
- Fix PLOGI vs ADISC race condition

Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_nportdisc.c
drivers/scsi/lpfc/lpfc_vport.c

index 43049b9d64c9ea5d715d8cbc259b462a6eb2b9fa..2e24b4fe2be546936e4e3795fb5d25efd855618a 100644 (file)
@@ -1555,6 +1555,83 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        return 0;
 }
 
+/**
+ * lpfc_rscn_disc: Perform rscn discovery for a vport.
+ * @vport: pointer to a host virtual N_Port data structure.
+ *
+ * This routine performs Registration State Change Notification (RSCN)
+ * discovery for a @vport. If the @vport's node port recovery count is not
+ * zero, it will invoke the lpfc_els_disc_plogi() to perform PLOGI for all
+ * the nodes that need recovery. If none of the PLOGI were needed through
+ * the lpfc_els_disc_plogi() routine, the lpfc_end_rscn() routine shall be
+ * invoked to check and handle possible more RSCN came in during the period
+ * of processing the current ones.
+ **/
+static void
+lpfc_rscn_disc(struct lpfc_vport *vport)
+{
+       lpfc_can_disctmo(vport);
+
+       /* RSCN discovery */
+       /* go thru NPR nodes and issue ELS PLOGIs */
+       if (vport->fc_npr_cnt)
+               if (lpfc_els_disc_plogi(vport))
+                       return;
+
+       lpfc_end_rscn(vport);
+}
+
+/**
+ * lpfc_adisc_done: Complete the adisc phase of discovery.
+ * @vport: pointer to lpfc_vport hba data structure that finished all ADISCs.
+ *
+ * This function is called when the final ADISC is completed during discovery.
+ * This function handles clearing link attention or issuing reg_vpi depending
+ * on whether npiv is enabled. This function also kicks off the PLOGI phase of
+ * discovery.
+ * This function is called with no locks held.
+ **/
+static void
+lpfc_adisc_done(struct lpfc_vport *vport)
+{
+       struct Scsi_Host   *shost = lpfc_shost_from_vport(vport);
+       struct lpfc_hba   *phba = vport->phba;
+
+       /*
+        * For NPIV, cmpl_reg_vpi will set port_state to READY,
+        * and continue discovery.
+        */
+       if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
+           !(vport->fc_flag & FC_RSCN_MODE)) {
+               lpfc_issue_reg_vpi(phba, vport);
+               return;
+       }
+       /*
+       * For SLI2, we need to set port_state to READY
+       * and continue discovery.
+       */
+       if (vport->port_state < LPFC_VPORT_READY) {
+               /* If we get here, there is nothing to ADISC */
+               if (vport->port_type == LPFC_PHYSICAL_PORT)
+                       lpfc_issue_clear_la(phba, vport);
+               if (!(vport->fc_flag & FC_ABORT_DISCOVERY)) {
+                       vport->num_disc_nodes = 0;
+                       /* go thru NPR list, issue ELS PLOGIs */
+                       if (vport->fc_npr_cnt)
+                               lpfc_els_disc_plogi(vport);
+                       if (!vport->num_disc_nodes) {
+                               spin_lock_irq(shost->host_lock);
+                               vport->fc_flag &= ~FC_NDISC_ACTIVE;
+                               spin_unlock_irq(shost->host_lock);
+                               lpfc_can_disctmo(vport);
+                               lpfc_end_rscn(vport);
+                       }
+               }
+               vport->port_state = LPFC_VPORT_READY;
+       } else
+               lpfc_rscn_disc(vport);
+}
+
 /**
  * lpfc_more_adisc: Issue more adisc as needed.
  * @vport: pointer to a host virtual N_Port data structure.
@@ -1583,35 +1660,11 @@ lpfc_more_adisc(struct lpfc_vport *vport)
                /* go thru NPR nodes and issue any remaining ELS ADISCs */
                sentadisc = lpfc_els_disc_adisc(vport);
        }
+       if (!vport->num_disc_nodes)
+               lpfc_adisc_done(vport);
        return;
 }
 
-/**
- * lpfc_rscn_disc: Perform rscn discovery for a vport.
- * @vport: pointer to a host virtual N_Port data structure.
- *
- * This routine performs Registration State Change Notification (RSCN)
- * discovery for a @vport. If the @vport's node port recovery count is not
- * zero, it will invoke the lpfc_els_disc_plogi() to perform PLOGI for all
- * the nodes that need recovery. If none of the PLOGI were needed through
- * the lpfc_els_disc_plogi() routine, the lpfc_end_rscn() routine shall be
- * invoked to check and handle possible more RSCN came in during the period
- * of processing the current ones.
- **/
-static void
-lpfc_rscn_disc(struct lpfc_vport *vport)
-{
-       lpfc_can_disctmo(vport);
-
-       /* RSCN discovery */
-       /* go thru NPR nodes and issue ELS PLOGIs */
-       if (vport->fc_npr_cnt)
-               if (lpfc_els_disc_plogi(vport))
-                       return;
-
-       lpfc_end_rscn(vport);
-}
-
 /**
  * lpfc_cmpl_els_adisc: Completion callback function for adisc.
  * @phba: pointer to lpfc hba data structure.
@@ -1692,52 +1745,9 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                lpfc_disc_state_machine(vport, ndlp, cmdiocb,
                                        NLP_EVT_CMPL_ADISC);
 
-       if (disc && vport->num_disc_nodes) {
-               /* Check to see if there are more ADISCs to be sent */
+       /* Check to see if there are more ADISCs to be sent */
+       if (disc && vport->num_disc_nodes)
                lpfc_more_adisc(vport);
-
-               /* Check to see if we are done with ADISC authentication */
-               if (vport->num_disc_nodes == 0) {
-                       /* If we get here, there is nothing left to ADISC */
-                       /*
-                        * For NPIV, cmpl_reg_vpi will set port_state to READY,
-                        * and continue discovery.
-                        */
-                       if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
-                          !(vport->fc_flag & FC_RSCN_MODE)) {
-                               lpfc_issue_reg_vpi(phba, vport);
-                               goto out;
-                       }
-                       /*
-                        * For SLI2, we need to set port_state to READY
-                        * and continue discovery.
-                        */
-                       if (vport->port_state < LPFC_VPORT_READY) {
-                               /* If we get here, there is nothing to ADISC */
-                               if (vport->port_type == LPFC_PHYSICAL_PORT)
-                                       lpfc_issue_clear_la(phba, vport);
-
-                               if (!(vport->fc_flag & FC_ABORT_DISCOVERY)) {
-                                       vport->num_disc_nodes = 0;
-                                       /* go thru NPR list, issue ELS PLOGIs */
-                                       if (vport->fc_npr_cnt)
-                                               lpfc_els_disc_plogi(vport);
-
-                                       if (!vport->num_disc_nodes) {
-                                               spin_lock_irq(shost->host_lock);
-                                               vport->fc_flag &=
-                                                       ~FC_NDISC_ACTIVE;
-                                               spin_unlock_irq(
-                                                       shost->host_lock);
-                                               lpfc_can_disctmo(vport);
-                                       }
-                               }
-                               vport->port_state = LPFC_VPORT_READY;
-                       } else {
-                               lpfc_rscn_disc(vport);
-                       }
-               }
-       }
 out:
        lpfc_els_free_iocb(phba, cmdiocb);
        return;
@@ -2258,19 +2268,16 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
                        if (vport->port_state < LPFC_VPORT_READY) {
                                /* Check if there are more ADISCs to be sent */
                                lpfc_more_adisc(vport);
-                               if ((vport->num_disc_nodes == 0) &&
-                                   (vport->fc_npr_cnt))
-                                       lpfc_els_disc_plogi(vport);
                        } else {
                                /* Check if there are more PLOGIs to be sent */
                                lpfc_more_plogi(vport);
-                       }
-                       if (vport->num_disc_nodes == 0) {
-                               spin_lock_irq(shost->host_lock);
-                               vport->fc_flag &= ~FC_NDISC_ACTIVE;
-                               spin_unlock_irq(shost->host_lock);
-                               lpfc_can_disctmo(vport);
-                               lpfc_end_rscn(vport);
+                               if (vport->num_disc_nodes == 0) {
+                                       spin_lock_irq(shost->host_lock);
+                                       vport->fc_flag &= ~FC_NDISC_ACTIVE;
+                                       spin_unlock_irq(shost->host_lock);
+                                       lpfc_can_disctmo(vport);
+                                       lpfc_end_rscn(vport);
+                               }
                        }
                }
        }
@@ -4480,14 +4487,9 @@ lpfc_els_rcv_rps(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
        struct ls_rjt stat;
 
        if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
-           (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) {
-               stat.un.b.lsRjtRsvd0 = 0;
-               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
-               stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
-               stat.un.b.vendorUnique = 0;
-               lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
-                       NULL);
-       }
+           (ndlp->nlp_state != NLP_STE_MAPPED_NODE))
+               /* reject the unsolicited RPS request and done with it */
+               goto reject_out;
 
        pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
        lp = (uint32_t *) pcmd->virt;
@@ -4520,6 +4522,9 @@ lpfc_els_rcv_rps(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
                        mempool_free(mbox, phba->mbox_mem_pool);
                }
        }
+
+reject_out:
+       /* issue rejection response */
        stat.un.b.lsRjtRsvd0 = 0;
        stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
        stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
@@ -4629,12 +4634,15 @@ lpfc_els_rcv_rpl(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 
        if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
            (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) {
+               /* issue rejection response */
                stat.un.b.lsRjtRsvd0 = 0;
                stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
                stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
                stat.un.b.vendorUnique = 0;
                lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
                        NULL);
+               /* rejected the unsolicited RPL request and done with it */
+               return 0;
        }
 
        pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
index b9d553c2ac4db6ce677fa0d8906051e63ad62163..93fd09daca8757523052ecdb004b5736d8232f5d 100644 (file)
@@ -1580,14 +1580,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
                lpfc_disc_state_machine(vport, ndlp, NULL,
                                             NLP_EVT_DEVICE_RM);
 
-               /* nlp_type zero is not defined, nlp_flag zero also not defined,
-                * nlp_state is unused, this happens when
-                * an initiator has logged
-                * into us so cleanup this ndlp.
-                */
-               if ((ndlp->nlp_type == 0) && (ndlp->nlp_flag == 0) &&
-                       (ndlp->nlp_state == 0))
-                       lpfc_nlp_put(ndlp);
        }
 
        /* At this point, ALL ndlp's should be gone
index 6688a8689b568babe82657465c1db693b5554578..705c4ae1bdc3a792ea945fa65f8e0f9c551d9aae 100644 (file)
@@ -1003,20 +1003,8 @@ lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                        spin_lock_irq(shost->host_lock);
                        ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
                        spin_unlock_irq(shost->host_lock);
-
-                       if (vport->num_disc_nodes) {
+                       if (vport->num_disc_nodes)
                                lpfc_more_adisc(vport);
-                               if ((vport->num_disc_nodes == 0) &&
-                                   (vport->fc_npr_cnt))
-                                       lpfc_els_disc_plogi(vport);
-                               if (vport->num_disc_nodes == 0) {
-                                       spin_lock_irq(shost->host_lock);
-                                       vport->fc_flag &= ~FC_NDISC_ACTIVE;
-                                       spin_unlock_irq(shost->host_lock);
-                                       lpfc_can_disctmo(vport);
-                                       lpfc_end_rscn(vport);
-                               }
-                       }
                }
                return ndlp->nlp_state;
        }
index 109f89d9883021fca9ffb87f5f279f5734481440..ad0f65313878c8f6be6cf386034873f99da082d7 100644 (file)
@@ -204,6 +204,77 @@ lpfc_unique_wwpn(struct lpfc_hba *phba, struct lpfc_vport *new_vport)
        return 1;
 }
 
+/**
+ * lpfc_discovery_wait: Wait for driver discovery to quiesce.
+ * @vport: The virtual port for which this call is being executed.
+ *
+ * This driver calls this routine specifically from lpfc_vport_delete
+ * to enforce a synchronous execution of vport
+ * delete relative to discovery activities.  The
+ * lpfc_vport_delete routine should not return until it
+ * can reasonably guarantee that discovery has quiesced.
+ * Post FDISC LOGO, the driver must wait until its SAN teardown is
+ * complete and all resources recovered before allowing
+ * cleanup.
+ *
+ * This routine does not require any locks held.
+ **/
+static void lpfc_discovery_wait(struct lpfc_vport *vport)
+{
+       struct lpfc_hba *phba = vport->phba;
+       uint32_t wait_flags = 0;
+       unsigned long wait_time_max;
+       unsigned long start_time;
+
+       wait_flags = FC_RSCN_MODE | FC_RSCN_DISCOVERY | FC_NLP_MORE |
+                    FC_RSCN_DEFERRED | FC_NDISC_ACTIVE | FC_DISC_TMO;
+
+       /*
+        * The time constraint on this loop is a balance between the
+        * fabric RA_TOV value and dev_loss tmo.  The driver's
+        * devloss_tmo is 10 giving this loop a 3x multiplier minimally.
+        */
+       wait_time_max = msecs_to_jiffies(((phba->fc_ratov * 3) + 3) * 1000);
+       wait_time_max += jiffies;
+       start_time = jiffies;
+       while (time_before(jiffies, wait_time_max)) {
+               if ((vport->num_disc_nodes > 0)    ||
+                   (vport->fc_flag & wait_flags)  ||
+                   ((vport->port_state > LPFC_VPORT_FAILED) &&
+                    (vport->port_state < LPFC_VPORT_READY))) {
+                       lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
+                                       "1833 Vport discovery quiesce Wait:"
+                                       " vpi x%x state x%x fc_flags x%x"
+                                       " num_nodes x%x, waiting 1000 msecs"
+                                       " total wait msecs x%x\n",
+                                       vport->vpi, vport->port_state,
+                                       vport->fc_flag, vport->num_disc_nodes,
+                                       jiffies_to_msecs(jiffies - start_time));
+                       msleep(1000);
+               } else {
+                       /* Base case.  Wait variants satisfied.  Break out */
+                       lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
+                                        "1834 Vport discovery quiesced:"
+                                        " vpi x%x state x%x fc_flags x%x"
+                                        " wait msecs x%x\n",
+                                        vport->vpi, vport->port_state,
+                                        vport->fc_flag,
+                                        jiffies_to_msecs(jiffies
+                                               - start_time));
+                       break;
+               }
+       }
+
+       if (time_after(jiffies, wait_time_max))
+               lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+                               "1835 Vport discovery quiesce failed:"
+                               " vpi x%x state x%x fc_flags x%x"
+                               " wait msecs x%x\n",
+                               vport->vpi, vport->port_state,
+                               vport->fc_flag,
+                               jiffies_to_msecs(jiffies - start_time));
+}
+
 int
 lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
 {
@@ -602,6 +673,9 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
                                timeout = schedule_timeout(timeout);
        }
 
+       if (!(phba->pport->load_flag & FC_UNLOADING))
+               lpfc_discovery_wait(vport);
+
 skip_logo:
        lpfc_cleanup(vport);
        lpfc_sli_host_down(vport);