[SCSI] lpfc 8.1.2: Misc FC Discovery changes :

Misc FC Discovery changes :
   - Added FC_BYPASSED_MODE statistic
   - Corrected some log message data
   - Fix up Discovery infrastructure to support FAN:
       Allow Fabric entities to flow thru DSM
       Fix up linkup/linkdown unregister login processing for Fabric entities
       Clean up Discovery code
       Utilize nodev_tmo for Fabric entities
   - Use of 3 * ratov for CT handling timeouts
   - Fix up DSM to make more appropriate decisions and clean up code.

Signed-off-by: Jamie Wellnitz <Jamie.Wellnitz@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index a1f751e..5c39617 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -283,16 +283,18 @@
 {
 	struct lpfc_sli       *psli;
 	struct lpfc_nodelist  *ndlp, *next_ndlp;
-	struct list_head *listp;
-	struct list_head *node_list[7];
+	struct list_head *listp, *node_list[7];
 	LPFC_MBOXQ_t     *mb;
 	int               rc, i;
 
 	psli = &phba->sli;
 
-	spin_lock_irq(phba->host->host_lock);
-	phba->hba_state = LPFC_LINK_DOWN;
-	spin_unlock_irq(phba->host->host_lock);
+	/* sysfs or selective reset may call this routine to clean up */
+	if (phba->hba_state > LPFC_LINK_DOWN) {
+		spin_lock_irq(phba->host->host_lock);
+		phba->hba_state = LPFC_LINK_DOWN;
+		spin_unlock_irq(phba->host->host_lock);
+	}
 
 	/* Clean up any firmware default rpi's */
 	if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
@@ -324,32 +326,20 @@
 			continue;
 
 		list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {
-			/* Fabric nodes are not handled thru state machine for
-			   link down */
-			if (ndlp->nlp_type & NLP_FABRIC) {
-				/* Remove ALL Fabric nodes except Fabric_DID */
-				if (ndlp->nlp_DID != Fabric_DID) {
-					/* Take it off current list and free */
-					lpfc_nlp_list(phba, ndlp,
-						NLP_NO_LIST);
-				}
-			}
-			else {
 
-				rc = lpfc_disc_state_machine(phba, ndlp, NULL,
-						     NLP_EVT_DEVICE_RECOVERY);
+			rc = lpfc_disc_state_machine(phba, ndlp, NULL,
+					     NLP_EVT_DEVICE_RECOVERY);
 
-				/* Check config parameter use-adisc or FCP-2 */
-				if ((rc != NLP_STE_FREED_NODE) &&
-					(phba->cfg_use_adisc == 0) &&
-					!(ndlp->nlp_fcp_info &
-						NLP_FCP_2_DEVICE)) {
-					/* We know we will have to relogin, so
-					 * unreglogin the rpi right now to fail
-					 * any outstanding I/Os quickly.
-					 */
-					lpfc_unreg_rpi(phba, ndlp);
-				}
+			/* Check config parameter use-adisc or FCP-2 */
+			if ((rc != NLP_STE_FREED_NODE) &&
+				(phba->cfg_use_adisc == 0) &&
+				!(ndlp->nlp_fcp_info &
+					NLP_FCP_2_DEVICE)) {
+				/* We know we will have to relogin, so
+				 * unreglogin the rpi right now to fail
+				 * any outstanding I/Os quickly.
+				 */
+				lpfc_unreg_rpi(phba, ndlp);
 			}
 		}
 	}
@@ -391,6 +381,8 @@
 lpfc_linkup(struct lpfc_hba * phba)
 {
 	struct lpfc_nodelist *ndlp, *next_ndlp;
+	struct list_head *listp, *node_list[7];
+	int i;
 
 	spin_lock_irq(phba->host->host_lock);
 	phba->hba_state = LPFC_LINK_UP;
@@ -401,14 +393,33 @@
 	spin_unlock_irq(phba->host->host_lock);
 
 
-	/*
-	 * Clean up old Fabric NLP_FABRIC logins.
-	 */
-	list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list,
-				nlp_listp) {
-		if (ndlp->nlp_DID == Fabric_DID) {
-			/* Take it off current list and free */
-			lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+	node_list[0] = &phba->fc_plogi_list;
+	node_list[1] = &phba->fc_adisc_list;
+	node_list[2] = &phba->fc_reglogin_list;
+	node_list[3] = &phba->fc_prli_list;
+	node_list[4] = &phba->fc_nlpunmap_list;
+	node_list[5] = &phba->fc_nlpmap_list;
+	node_list[6] = &phba->fc_npr_list;
+	for (i = 0; i < 7; i++) {
+		listp = node_list[i];
+		if (list_empty(listp))
+			continue;
+
+		list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {
+			if (phba->fc_flag & FC_LBIT) {
+				if (ndlp->nlp_type & NLP_FABRIC) {
+					/* On Linkup its safe to clean up the
+					 * ndlp from Fabric connections.
+					 */
+					lpfc_nlp_list(phba, ndlp,
+							NLP_UNUSED_LIST);
+				} else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
+					/* Fail outstanding IO now since device
+					 * is marked for PLOGI.
+					 */
+					lpfc_unreg_rpi(phba, ndlp);
+				}
+			}
 		}
 	}
 
@@ -784,6 +795,13 @@
 
 	memcpy(&phba->alpa_map[0], mp->virt, 128);
 
+	spin_lock_irq(phba->host->host_lock);
+	if (la->pb)
+		phba->fc_flag |= FC_BYPASSED_MODE;
+	else
+		phba->fc_flag &= ~FC_BYPASSED_MODE;
+	spin_unlock_irq(phba->host->host_lock);
+
 	if (((phba->fc_eventTag + 1) < la->eventTag) ||
 	     (phba->fc_eventTag == la->eventTag)) {
 		phba->fc_stat.LinkMultiEvent++;
@@ -904,32 +922,36 @@
 		 */
 		lpfc_issue_els_scr(phba, SCR_DID, 0);
 
-		/* Allocate a new node instance.  If the pool is empty, just
-		 * start the discovery process and skip the Nameserver login
-		 * process.  This is attempted again later on.  Otherwise, issue
-		 * a Port Login (PLOGI) to the NameServer
-		 */
-		if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL))
-		    == 0) {
-			lpfc_disc_start(phba);
-		} else {
-			lpfc_nlp_init(phba, ndlp, NameServer_DID);
-			ndlp->nlp_type |= NLP_FABRIC;
-			ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
-			lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
-			lpfc_issue_els_plogi(phba, ndlp, 0);
-			if (phba->cfg_fdmi_on) {
-				if ((ndlp_fdmi = mempool_alloc(
-						       phba->nlp_mem_pool,
-						       GFP_KERNEL))) {
-					lpfc_nlp_init(phba, ndlp_fdmi,
-						FDMI_DID);
-					ndlp_fdmi->nlp_type |= NLP_FABRIC;
-					ndlp_fdmi->nlp_state =
-					    NLP_STE_PLOGI_ISSUE;
-					lpfc_issue_els_plogi(phba, ndlp_fdmi,
-							     0);
-				}
+		ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID);
+		if (!ndlp) {
+			/* Allocate a new node instance. If the pool is empty,
+			 * start the discovery process and skip the Nameserver
+			 * login process.  This is attempted again later on.
+			 * Otherwise, issue a Port Login (PLOGI) to NameServer.
+			 */
+			ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC);
+			if (!ndlp) {
+				lpfc_disc_start(phba);
+				lpfc_mbuf_free(phba, mp->virt, mp->phys);
+				kfree(mp);
+				mempool_free( pmb, phba->mbox_mem_pool);
+				return;
+			} else {
+				lpfc_nlp_init(phba, ndlp, NameServer_DID);
+				ndlp->nlp_type |= NLP_FABRIC;
+			}
+		}
+		ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+		lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+		lpfc_issue_els_plogi(phba, ndlp, 0);
+		if (phba->cfg_fdmi_on) {
+			ndlp_fdmi = mempool_alloc(phba->nlp_mem_pool,
+								GFP_KERNEL);
+			if (ndlp_fdmi) {
+				lpfc_nlp_init(phba, ndlp_fdmi, FDMI_DID);
+				ndlp_fdmi->nlp_type |= NLP_FABRIC;
+				ndlp_fdmi->nlp_state = NLP_STE_PLOGI_ISSUE;
+				lpfc_issue_els_plogi(phba, ndlp_fdmi, 0);
 			}
 		}
 	}
@@ -937,7 +959,6 @@
 	lpfc_mbuf_free(phba, mp->virt, mp->phys);
 	kfree(mp);
 	mempool_free( pmb, phba->mbox_mem_pool);
-
 	return;
 }
 
@@ -1241,16 +1262,9 @@
 		list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list);
 		phba->fc_npr_cnt++;
 
-		/*
-		 * Sanity check for Fabric entity.
-		 * Set nodev_tmo for NPR state, for Fabric use 1 sec.
-		 */
-		if (nlp->nlp_type & NLP_FABRIC) {
-			mod_timer(&nlp->nlp_tmofunc, jiffies + HZ);
-		}
-		else {
+		if (!(nlp->nlp_flag & NLP_NODEV_TMO)) {
 			mod_timer(&nlp->nlp_tmofunc,
-			    jiffies + HZ * phba->cfg_nodev_tmo);
+		 			jiffies + HZ * phba->cfg_nodev_tmo);
 		}
 		spin_lock_irq(phba->host->host_lock);
 		nlp->nlp_flag |= NLP_NODEV_TMO;
@@ -1314,7 +1328,15 @@
 {
 	uint32_t tmo;
 
-	tmo = ((phba->fc_ratov * 2) + 1);
+	if (phba->hba_state == LPFC_LOCAL_CFG_LINK) {
+		/* For FAN, timeout should be greater then edtov */
+		tmo = (((phba->fc_edtov + 999) / 1000) + 1);
+	} else {
+		/* Normal discovery timeout should be > then ELS/CT timeout
+		 * FC spec states we need 3 * ratov for CT requests
+		 */
+		tmo = ((phba->fc_ratov * 3) + 3);
+	}
 
 	mod_timer(&phba->fc_disctmo, jiffies + HZ * tmo);
 	spin_lock_irq(phba->host->host_lock);
@@ -1846,8 +1868,9 @@
 	struct lpfc_nodelist *ndlp;
 	uint32_t flg;
 
-	if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) {
-		if ((phba->hba_state == LPFC_HBA_READY) &&
+	ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did);
+	if (!ndlp) {
+		if ((phba->fc_flag & FC_RSCN_MODE) &&
 		   ((lpfc_rscn_payload_check(phba, did) == 0)))
 			return NULL;
 		ndlp = (struct lpfc_nodelist *)
@@ -1860,10 +1883,23 @@
 		ndlp->nlp_flag |= NLP_NPR_2B_DISC;
 		return ndlp;
 	}
-	if ((phba->hba_state == LPFC_HBA_READY) &&
-	    (phba->fc_flag & FC_RSCN_MODE)) {
+	if (phba->fc_flag & FC_RSCN_MODE) {
 		if (lpfc_rscn_payload_check(phba, did)) {
 			ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+
+			/* Since this node is marked for discovery,
+			 * delay timeout is not needed.
+			 */
+			if (ndlp->nlp_flag & NLP_DELAY_TMO) {
+				ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+				spin_unlock_irq(phba->host->host_lock);
+				del_timer_sync(&ndlp->nlp_delayfunc);
+				spin_lock_irq(phba->host->host_lock);
+				if (!list_empty(&ndlp->els_retry_evt.
+						evt_listp))
+					list_del_init(&ndlp->els_retry_evt.
+				      		evt_listp);
+			}
 		}
 		else {
 			ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
@@ -1872,10 +1908,8 @@
 	}
 	else {
 		flg = ndlp->nlp_flag & NLP_LIST_MASK;
-		if ((flg == NLP_ADISC_LIST) ||
-		(flg == NLP_PLOGI_LIST)) {
+		if ((flg == NLP_ADISC_LIST) || (flg == NLP_PLOGI_LIST))
 			return NULL;
-		}
 		ndlp->nlp_state = NLP_STE_NPR_NODE;
 		lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
 		ndlp->nlp_flag |= NLP_NPR_2B_DISC;
@@ -2174,7 +2208,7 @@
 lpfc_disc_timeout_handler(struct lpfc_hba *phba)
 {
 	struct lpfc_sli *psli;
-	struct lpfc_nodelist *ndlp;
+	struct lpfc_nodelist *ndlp, *next_ndlp;
 	LPFC_MBOXQ_t *clearlambox, *initlinkmbox;
 	int rc, clrlaerr = 0;
 
@@ -2201,10 +2235,20 @@
 				 "%d:0221 FAN timeout\n",
 				 phba->brd_no);
 
-		/* Forget about FAN, Start discovery by sending a FLOGI
-		 * hba_state is identically LPFC_FLOGI while waiting for FLOGI
-		 * cmpl
-		 */
+		/* Start discovery by sending FLOGI, clean up old rpis */
+		list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+					nlp_listp) {
+			if (ndlp->nlp_type & NLP_FABRIC) {
+				/* Clean up the ndlp on Fabric connections */
+				lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+			}
+			else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
+				/* Fail outstanding IO now since device
+				 * is marked for PLOGI.
+				 */
+				lpfc_unreg_rpi(phba, ndlp);
+			}
+		}
 		phba->hba_state = LPFC_FLOGI;
 		lpfc_set_disctmo(phba);
 		lpfc_initial_flogi(phba);