[SCSI] lpfc: bug fixes

 Following the NPIV support, the following changes have been accumulated
 in the testing and qualification of the driver:

 - Fix affinity of ELS ring to slow/deferred event processing
 - Fix Ring attention masks
 - Defer dev_loss_tmo timeout handling to worker thread
 - Consolidate link down error classification for better error checking
 - Remove unused/deprecated nlp_initiator_tmr timer
 - Fix for async scan - move adapter init code back into pci_probe_one
   context. Fix async scan interfaces.
 - Expand validation of ability to create vports
 - Extract VPI resource cnt from firmware
 - Tuning of Login/Reject policies to better deal with overwhelmned targets
 - Misc ELS and discovery fixes
 - Export the npiv_enable attribute to sysfs
 - Mailbox handling fix
 - Add debugfs support
 - A few other small misc fixes:
    - wrong return values, double-frees, bad locking
 - Added adapter failure heartbeat

Signed-off-by: James Smart <James.Smart@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 94ee967..f2f4639 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -37,6 +37,7 @@
 #include "lpfc_logmsg.h"
 #include "lpfc_crtn.h"
 #include "lpfc_vport.h"
+#include "lpfc_debugfs.h"
 
 /* AlpaArray for assignment of scsid for scan-down and bind_method */
 static uint8_t lpfcAlpaArray[] = {
@@ -77,6 +78,10 @@
 
 	phba  = ndlp->vport->phba;
 
+	lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_RPORT,
+		"rport terminate: sid:x%x did:x%x flg:x%x",
+		ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);
+
 	if (ndlp->nlp_sid != NLP_NO_SID) {
 		lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
 			ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT);
@@ -93,12 +98,10 @@
 {
 	struct lpfc_rport_data *rdata;
 	struct lpfc_nodelist * ndlp;
-	uint8_t *name;
-	int warn_on = 0;
-	struct lpfc_hba *phba;
 	struct lpfc_vport *vport;
-	int  put_node;
-	int  put_rport;
+	struct lpfc_hba   *phba;
+	struct completion devloss_compl;
+	struct lpfc_work_evt *evtp;
 
 	rdata = rport->dd_data;
 	ndlp = rdata->pnode;
@@ -112,7 +115,70 @@
 		return;
 	}
 
+	vport = ndlp->vport;
+	phba  = vport->phba;
+
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
+		"rport devlosscb: sid:x%x did:x%x flg:x%x",
+		ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);
+
+	init_completion(&devloss_compl);
+	evtp = &ndlp->dev_loss_evt;
+
+	if (!list_empty(&evtp->evt_listp))
+		return;
+
+	spin_lock_irq(&phba->hbalock);
+	evtp->evt_arg1  = ndlp;
+	evtp->evt_arg2  = &devloss_compl;
+	evtp->evt       = LPFC_EVT_DEV_LOSS;
+	list_add_tail(&evtp->evt_listp, &phba->work_list);
+	if (phba->work_wait)
+		wake_up(phba->work_wait);
+
+	spin_unlock_irq(&phba->hbalock);
+
+	wait_for_completion(&devloss_compl);
+
+	return;
+}
+
+/*
+ * This function is called from the worker thread when dev_loss_tmo
+ * expire.
+ */
+void
+lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
+{
+	struct lpfc_rport_data *rdata;
+	struct fc_rport   *rport;
+	struct lpfc_vport *vport;
+	struct lpfc_hba   *phba;
+	uint8_t *name;
+	int warn_on = 0;
+
+	rport = ndlp->rport;
+
+	if (!rport)
+		return;
+
+	rdata = rport->dd_data;
+	name = (uint8_t *) &ndlp->nlp_portname;
+	vport = ndlp->vport;
+	phba  = vport->phba;
+
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
+		"rport devlosstmo:did:x%x type:x%x id:x%x",
+		ndlp->nlp_DID, ndlp->nlp_type, rport->scsi_target_id);
+
+	if (!(vport->load_flag & FC_UNLOADING) &&
+	    ndlp->nlp_state == NLP_STE_MAPPED_NODE)
+		return;
+
 	if (ndlp->nlp_type & NLP_FABRIC) {
+		int  put_node;
+		int  put_rport;
+
 		/* We will clean up these Nodes in linkup */
 		put_node = rdata->pnode != NULL;
 		put_rport = ndlp->rport != NULL;
@@ -125,15 +191,6 @@
 		return;
 	}
 
-	name = (uint8_t *)&ndlp->nlp_portname;
-	vport = ndlp->vport;
-	phba  = vport->phba;
-
-	if (!(vport->load_flag & FC_UNLOADING) &&
-	    ndlp->nlp_state == NLP_STE_MAPPED_NODE)
-		return;
-
-
 	if (ndlp->nlp_sid != NLP_NO_SID) {
 		warn_on = 1;
 		/* flush the target */
@@ -171,6 +228,9 @@
 	    (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE))
 		lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
 	else {
+		int  put_node;
+		int  put_rport;
+
 		put_node = rdata->pnode != NULL;
 		put_rport = ndlp->rport != NULL;
 		rdata->pnode = NULL;
@@ -180,7 +240,6 @@
 		if (put_rport)
 			put_device(&rport->dev);
 	}
-	return;
 }
 
 
@@ -206,12 +265,17 @@
 		spin_unlock_irq(&phba->hbalock);
 		free_evt = 1;
 		switch (evtp->evt) {
-		case LPFC_EVT_DEV_LOSS:
+		case LPFC_EVT_DEV_LOSS_DELAY:
 			free_evt = 0; /* evt is part of ndlp */
 			ndlp = (struct lpfc_nodelist *) (evtp->evt_arg1);
 			vport = ndlp->vport;
 			if (!vport)
 				break;
+
+			lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
+				"rport devlossdly:did:x%x flg:x%x",
+				ndlp->nlp_DID, ndlp->nlp_flag, 0);
+
 			if (!(vport->load_flag & FC_UNLOADING) &&
 			    !(ndlp->nlp_flag & NLP_DELAY_TMO) &&
 			    !(ndlp->nlp_flag & NLP_NPR_2B_DISC)) {
@@ -224,6 +288,14 @@
 			lpfc_els_retry_delay_handler(ndlp);
 			free_evt = 0; /* evt is part of ndlp */
 			break;
+		case LPFC_EVT_DEV_LOSS:
+			ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);
+			lpfc_nlp_get(ndlp);
+			lpfc_dev_loss_tmo_handler(ndlp);
+			free_evt = 0;
+			complete((struct completion *)(evtp->evt_arg2));
+			lpfc_nlp_put(ndlp);
+			break;
 		case LPFC_EVT_ONLINE:
 			if (phba->link_state < LPFC_LINK_DOWN)
 				*(int *) (evtp->evt_arg1) = lpfc_online(phba);
@@ -272,13 +344,12 @@
 
 }
 
-static void
+void
 lpfc_work_done(struct lpfc_hba *phba)
 {
 	struct lpfc_sli_ring *pring;
-	uint32_t ha_copy, control, work_port_events;
+	uint32_t ha_copy, status, control, work_port_events;
 	struct lpfc_vport *vport;
-	int i;
 
 	spin_lock_irq(&phba->hbalock);
 	ha_copy = phba->work_ha;
@@ -310,6 +381,9 @@
 		if (work_port_events & WORKER_ELS_TMO)
 			lpfc_els_timeout_handler(vport);
 
+		if (work_port_events & WORKER_HB_TMO)
+			lpfc_hb_timeout_handler(phba);
+
 		if (work_port_events & WORKER_MBOX_TMO)
 			lpfc_mbox_timeout_handler(phba);
 
@@ -333,30 +407,31 @@
 	}
 	spin_unlock_irq(&phba->hbalock);
 
-	for (i = 0; i < phba->sli.num_rings; i++, ha_copy >>= 4) {
-		pring = &phba->sli.ring[i];
-		if ((ha_copy & HA_RXATT)
-		    || (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
-			if (pring->flag & LPFC_STOP_IOCB_MASK) {
-				pring->flag |= LPFC_DEFERRED_RING_EVENT;
-			} else {
-				lpfc_sli_handle_slow_ring_event(phba, pring,
-								(ha_copy &
-								 HA_RXMASK));
-				pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
-			}
-			/*
-			 * Turn on Ring interrupts
-			 */
-			spin_lock_irq(&phba->hbalock);
-			control = readl(phba->HCregaddr);
-			control |= (HC_R0INT_ENA << i);
+	pring = &phba->sli.ring[LPFC_ELS_RING];
+	status = (ha_copy & (HA_RXMASK  << (4*LPFC_ELS_RING)));
+	status >>= (4*LPFC_ELS_RING);
+	if ((status & HA_RXMASK)
+		|| (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
+		if (pring->flag & LPFC_STOP_IOCB_MASK) {
+			pring->flag |= LPFC_DEFERRED_RING_EVENT;
+		} else {
+			lpfc_sli_handle_slow_ring_event(phba, pring,
+							(status &
+							 HA_RXMASK));
+			pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
+		}
+		/*
+		 * Turn on Ring interrupts
+		 */
+		spin_lock_irq(&phba->hbalock);
+		control = readl(phba->HCregaddr);
+		if (!(control & (HC_R0INT_ENA << LPFC_ELS_RING))) {
+			control |= (HC_R0INT_ENA << LPFC_ELS_RING);
 			writel(control, phba->HCregaddr);
 			readl(phba->HCregaddr); /* flush */
-			spin_unlock_irq(&phba->hbalock);
 		}
+		spin_unlock_irq(&phba->hbalock);
 	}
-
 	lpfc_work_list_done(phba);
 }
 
@@ -365,7 +440,7 @@
 {
 	struct lpfc_vport *vport;
 	struct lpfc_sli_ring *pring;
-	int i, rc = 0;
+	int rc = 0;
 
 	spin_lock_irq(&phba->hbalock);
 	list_for_each_entry(vport, &phba->port_list, listentry) {
@@ -380,13 +455,10 @@
 		rc = 1;
 		goto exit;
 	}
-	for (i = 0; i < phba->sli.num_rings; i++) {
-		pring = &phba->sli.ring[i];
-		if (pring->flag & LPFC_DEFERRED_RING_EVENT) {
-			rc = 1;
-			goto exit;
-		}
-	}
+
+	pring = &phba->sli.ring[LPFC_ELS_RING];
+	if (pring->flag & LPFC_DEFERRED_RING_EVENT)
+		rc = 1;
 exit:
 	if (rc)
 		phba->work_found++;
@@ -506,6 +578,10 @@
 
 	fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_LINKDOWN, 0);
 
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+		"Link Down:       state:x%x rtry:x%x flg:x%x",
+		vport->port_state, vport->fc_ns_retry, vport->fc_flag);
+
 	/* Cleanup any outstanding RSCN activity */
 	lpfc_els_flush_rscn(vport);
 
@@ -617,6 +693,10 @@
 	if ((vport->load_flag & FC_UNLOADING) != 0)
 		return;
 
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+		"Link Up:         top:x%x speed:x%x flg:x%x",
+		phba->fc_topology, phba->fc_linkspeed, phba->link_flag);
+
 	/* If NPIV is not enabled, only bring the physical port up */
 	if (!(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
 		(vport != phba->pport))
@@ -935,7 +1015,7 @@
 		}
 	} else {
 		if (!(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
-			if (phba->max_vpi && lpfc_npiv_enable &&
+			if (phba->max_vpi && phba->cfg_npiv_enable &&
 			   (phba->sli_rev == 3))
 				phba->sli3_options |= LPFC_SLI3_NPIV_ENABLED;
 		}
@@ -1124,8 +1204,6 @@
 				"mb status = 0x%x\n",
 				phba->brd_no, vport->vpi, mb->mbxStatus);
 		break;
-	default:
-		phba->vpi_cnt--;
 	}
 	vport->unreg_vpi_cmpl = VPORT_OK;
 	mempool_free(pmb, phba->mbox_mem_pool);
@@ -1182,7 +1260,6 @@
 		vport->fc_myDID = 0;
 		goto out;
 	}
-	phba->vpi_cnt++;
 
 	vport->num_disc_nodes = 0;
 	/* go thru NPR list and issue ELS PLOGIs */
@@ -1257,16 +1334,13 @@
 
 			if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
 				lpfc_initial_fdisc(next_vport);
-			else {
-				if (phba->sli3_options &
-					LPFC_SLI3_NPIV_ENABLED) {
-					lpfc_vport_set_state(vport,
-						FC_VPORT_NO_FABRIC_SUPP);
-					lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+			else if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
+				lpfc_vport_set_state(vport,
+						     FC_VPORT_NO_FABRIC_SUPP);
+				lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
 						"%d (%d):0259 No NPIV Fabric "
 						"support\n",
 						phba->brd_no, vport->vpi);
-				}
 			}
 		}
 		lpfc_do_scr_ns_plogi(phba, vport);
@@ -1377,6 +1451,11 @@
 	    ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp) {
 		lpfc_nlp_put(ndlp);
 	}
+
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
+		"rport add:       did:x%x flg:x%x type x%x",
+		ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
+
 	ndlp->rport = rport = fc_remote_port_add(shost, 0, &rport_ids);
 	if (!rport || !get_device(&rport->dev)) {
 		dev_printk(KERN_WARNING, &phba->pcidev->dev,
@@ -1394,7 +1473,6 @@
 		rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
 	if (ndlp->nlp_type & NLP_FCP_INITIATOR)
 		rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
-	del_timer_sync(&ndlp->nlp_initiator_tmr);
 
 
 	if (rport_ids.roles !=  FC_RPORT_ROLE_UNKNOWN)
@@ -1412,6 +1490,10 @@
 {
 	struct fc_rport *rport = ndlp->rport;
 
+	lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_RPORT,
+		"rport delete:    did:x%x flg:x%x type x%x",
+		ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
+
 	fc_remote_port_delete(rport);
 
 	return;
@@ -1478,20 +1560,19 @@
 	if (new_state ==  NLP_STE_MAPPED_NODE ||
 	    new_state == NLP_STE_UNMAPPED_NODE) {
 		vport->phba->nport_event_cnt++;
-			/*
-			 * Tell the fc transport about the port, if we haven't
-			 * already. If we have, and it's a scsi entity, be
-			 * sure to unblock any attached scsi devices
-			 */
-			lpfc_register_remote_port(vport, ndlp);
+		/*
+		 * Tell the fc transport about the port, if we haven't
+		 * already. If we have, and it's a scsi entity, be
+		 * sure to unblock any attached scsi devices
+		 */
+		lpfc_register_remote_port(vport, ndlp);
 	}
-
-			/*
-			 * if we added to Mapped list, but the remote port
-			 * registration failed or assigned a target id outside
-			 * our presentable range - move the node to the
-			 * Unmapped List
-			 */
+	/*
+	 * if we added to Mapped list, but the remote port
+	 * registration failed or assigned a target id outside
+	 * our presentable range - move the node to the
+	 * Unmapped List
+	 */
 	if (new_state == NLP_STE_MAPPED_NODE &&
 	    (!ndlp->rport ||
 	     ndlp->rport->scsi_target_id == -1 ||
@@ -1533,11 +1614,16 @@
 	char name1[16], name2[16];
 
 	lpfc_printf_log(vport->phba, KERN_INFO, LOG_NODE,
-			"%d:0904 NPort state transition x%06x, %s -> %s\n",
-			vport->phba->brd_no,
+			"%d (%d):0904 NPort state transition x%06x, %s -> %s\n",
+			vport->phba->brd_no, vport->vpi,
 			ndlp->nlp_DID,
 			lpfc_nlp_state_name(name1, sizeof(name1), old_state),
 			lpfc_nlp_state_name(name2, sizeof(name2), state));
+
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
+		"node statechg    did:x%x old:%d ste:%d",
+		ndlp->nlp_DID, old_state, state);
+
 	if (old_state == NLP_STE_NPR_NODE &&
 	    (ndlp->nlp_flag & NLP_DELAY_TMO) != 0 &&
 	    state != NLP_STE_NPR_NODE)
@@ -1571,7 +1657,8 @@
 	spin_lock_irq(shost->host_lock);
 	list_del_init(&ndlp->nlp_listp);
 	spin_unlock_irq(shost->host_lock);
-	lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, 0);
+	lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
+			       NLP_STE_UNUSED_NODE);
 }
 
 void
@@ -1585,6 +1672,7 @@
 		lpfc_nlp_counters(vport, ndlp->nlp_state, -1);
 	spin_lock_irq(shost->host_lock);
 	list_del_init(&ndlp->nlp_listp);
+	ndlp->nlp_flag &= ~NLP_TARGET_REMOVE;
 	spin_unlock_irq(shost->host_lock);
 	lpfc_nlp_put(ndlp);
 }
@@ -1609,6 +1697,13 @@
 		tmo = ((phba->fc_ratov * 3) + 3);
 	}
 
+
+	if (!timer_pending(&vport->fc_disctmo)) {
+		lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+			"set disc timer:  tmo:x%x state:x%x flg:x%x",
+			tmo, vport->port_state, vport->fc_flag);
+	}
+
 	mod_timer(&vport->fc_disctmo, jiffies + HZ * tmo);
 	spin_lock_irq(shost->host_lock);
 	vport->fc_flag |= FC_DISC_TMO;
@@ -1635,6 +1730,10 @@
 	struct lpfc_hba  *phba = vport->phba;
 	unsigned long iflags;
 
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+		"can disc timer:  state:x%x rtry:x%x flg:x%x",
+		vport->port_state, vport->fc_ns_retry, vport->fc_flag);
+
 	/* Turn off discovery timer if its running */
 	if (vport->fc_flag & FC_DISC_TMO) {
 		spin_lock_irqsave(shost->host_lock, iflags);
@@ -1898,13 +1997,17 @@
 
 	ndlp->nlp_last_elscmd = 0;
 	del_timer_sync(&ndlp->nlp_delayfunc);
-	del_timer_sync(&ndlp->nlp_initiator_tmr);
 
 	if (!list_empty(&ndlp->els_retry_evt.evt_listp))
 		list_del_init(&ndlp->els_retry_evt.evt_listp);
 	if (!list_empty(&ndlp->dev_loss_evt.evt_listp))
 		list_del_init(&ndlp->dev_loss_evt.evt_listp);
 
+	if (!list_empty(&ndlp->dev_loss_evt.evt_listp)) {
+		list_del_init(&ndlp->dev_loss_evt.evt_listp);
+		complete((struct completion *)(ndlp->dev_loss_evt.evt_arg2));
+	}
+
 	lpfc_unreg_rpi(vport, ndlp);
 
 	return 0;
@@ -2418,6 +2521,10 @@
 	vport->fc_flag &= ~FC_DISC_TMO;
 	spin_unlock_irq(shost->host_lock);
 
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+		"disc timeout:    state:x%x rtry:x%x flg:x%x",
+		vport->port_state, vport->fc_ns_retry, vport->fc_flag);
+
 	switch (vport->port_state) {
 
 	case LPFC_LOCAL_CFG_LINK:
@@ -2743,7 +2850,7 @@
 	spin_lock_irq(shost->host_lock);
 	ndlp = __lpfc_find_node(vport, lpfc_filter_by_wwpn, wwpn);
 	spin_unlock_irq(shost->host_lock);
-	return NULL;
+	return ndlp;
 }
 
 void
@@ -2764,7 +2871,7 @@
 	}
 
 	evtp->evt_arg1  = ndlp;
-	evtp->evt       = LPFC_EVT_DEV_LOSS;
+	evtp->evt       = LPFC_EVT_DEV_LOSS_DELAY;
 	list_add_tail(&evtp->evt_listp, &phba->work_list);
 	if (phba->work_wait)
 		lpfc_worker_wake_up(phba);
@@ -2779,9 +2886,6 @@
 	memset(ndlp, 0, sizeof (struct lpfc_nodelist));
 	INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
 	INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
-	init_timer(&ndlp->nlp_initiator_tmr);
-	ndlp->nlp_initiator_tmr.function = lpfc_dev_loss_delay;
-	ndlp->nlp_initiator_tmr.data = (unsigned long)ndlp;
 	init_timer(&ndlp->nlp_delayfunc);
 	ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
 	ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
@@ -2790,6 +2894,11 @@
 	ndlp->nlp_sid = NLP_NO_SID;
 	INIT_LIST_HEAD(&ndlp->nlp_listp);
 	kref_init(&ndlp->kref);
+
+	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
+		"node init:       did:x%x",
+		ndlp->nlp_DID, 0, 0);
+
 	return;
 }
 
@@ -2798,6 +2907,11 @@
 {
 	struct lpfc_nodelist *ndlp = container_of(kref, struct lpfc_nodelist,
 						  kref);
+
+	lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
+		"node release:    did:x%x flg:x%x type:x%x",
+		ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
+
 	lpfc_nlp_remove(ndlp->vport, ndlp);
 	mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool);
 }