Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3479

kernel-2.6.18-194.11.1.el5.src.rpm

From: Rob Evers <revers@redhat.com>
Date: Tue, 7 Apr 2009 15:24:32 -0400
Subject: [scsi] lpfc: update to version 8.2.0.40
Message-id: 20090407192200.14572.14086.sendpatchset@localhost.localdomain
O-Subject: [RHEL5.4 PATCH V3 3/4] Update lpfc to version 8.2.0.40
Bugzilla: 476738

https://bugzilla.redhat.com/show_bug.cgi?id=476738

Update lpfc from 8.2.0.39 to 8.2.0.40

--

diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 75262ab..ee5ac6a 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -56,6 +56,9 @@ struct lpfc_sli2_slim;
 #define LPFC_HB_MBOX_INTERVAL   5	/* Heart beat interval in seconds. */
 #define LPFC_HB_MBOX_TIMEOUT    30	/* Heart beat timeout  in seconds. */
 
+/* Error Attention event polling interval */
+#define LPFC_ERATT_POLL_INTERVAL	5 /* EATT poll interval in seconds */
+
 /* Define macros for 64 bit support */
 #define putPaddrLow(addr)    ((uint32_t) (0xffffffff & (u64)(addr)))
 #define putPaddrHigh(addr)   ((uint32_t) (0xffffffff & (((u64)(addr))>>32)))
@@ -570,6 +573,10 @@ struct lpfc_hba {
 #define LS_NPIV_FAB_SUPPORTED 0x2	/* Fabric supports NPIV */
 #define LS_IGNORE_ERATT       0x4	/* intr handler should ignore ERATT */
 
+	uint32_t hba_flag;      /* hba generic flags */
+#define HBA_ERATT_HANDLED	0x1  /* This flag is set when eratt handled */
+#define DEFER_ERATT		0x80 /* Deferred error attention in progress */
+
 	struct lpfc_sli2_slim *slim2p;
 	struct lpfc_dmabuf hbqslimp;
 
@@ -687,6 +694,7 @@ struct lpfc_hba {
 	uint8_t soft_wwn_enable;
 
 	struct timer_list fcp_poll_timer;
+	struct timer_list eratt_poll;   /* Error attention polling timer.*/
 
 	/*
 	 * stat  counters
@@ -818,3 +826,24 @@ lpfc_worker_wake_up(struct lpfc_hba *phba)
 	return;
 }
 
+static inline void
+lpfc_sli_read_hs(struct lpfc_hba *phba)
+{
+	/*
+	 * There was a link/board error. Read the status register to retrieve
+	 * the error event and process it.
+	 */
+	phba->sli.slistat.err_attn_event++;
+
+	/* Save status info */
+	phba->work_hs = readl(phba->HSregaddr);
+	phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
+	phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
+
+	/* Clear chip Host Attention error bit */
+	writel(HA_ERATT, phba->HAregaddr);
+	readl(phba->HAregaddr); /* flush */
+	phba->pport->stopped = 1;
+
+	return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index e70c034..2f6b285 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1893,7 +1893,7 @@ lpfc_stat_data_ctrl_store(struct class_device *cdev, const char *buf,
 	unsigned long base, step, bucket_type;
 
 	if (!strncmp(buf, "setbucket", strlen("setbucket"))) {
-		if (strlen(buf) > LPFC_MAX_DATA_CTRL_LEN)
+		if (strlen(buf) > (LPFC_MAX_DATA_CTRL_LEN - 1))
 			return -EINVAL;
 
 		strcpy(bucket_data,buf);
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 198707c..81c0949 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -201,7 +201,7 @@ void lpfc_els_hbq_free(struct lpfc_hba *, struct hbq_dmabuf *);
 int lpfc_mem_alloc(struct lpfc_hba *);
 void lpfc_mem_free(struct lpfc_hba *);
 void lpfc_stop_vport_timers(struct lpfc_vport *);
-
+void lpfc_poll_eratt(unsigned long);
 void lpfc_poll_timeout(unsigned long ptr);
 void lpfc_poll_start_timer(struct lpfc_hba * phba);
 void lpfc_sli_poll_fcp_ring(struct lpfc_hba * hba);
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index a4521d2..694e415 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -2318,9 +2318,13 @@ lpfc_unreg_all_rpis(struct lpfc_vport *vport)
 		mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
 		mbox->context1 = NULL;
 		rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
-		if (rc == MBX_NOT_FINISHED) {
+		if (rc != MBX_TIMEOUT)
 			mempool_free(mbox, phba->mbox_mem_pool);
-		}
+
+		if ((rc == MBX_TIMEOUT) || (rc == MBX_NOT_FINISHED))
+			lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_VPORT,
+				"1836 Could not issue "
+				"unreg_login(all_rpis) status %d\n", rc);
 	}
 }
 
@@ -2338,12 +2342,14 @@ lpfc_unreg_default_rpis(struct lpfc_vport *vport)
 		mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
 		mbox->context1 = NULL;
 		rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
-		if (rc == MBX_NOT_FINISHED) {
+		if (rc != MBX_TIMEOUT)
+			mempool_free(mbox, phba->mbox_mem_pool);
+
+		if ((rc == MBX_TIMEOUT) || (rc == MBX_NOT_FINISHED))
 			lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_VPORT,
 					 "1815 Could not issue "
-					 "unreg_did (default rpis)\n");
-			mempool_free(mbox, phba->mbox_mem_pool);
-		}
+					 "unreg_did (default rpis) status %d\n",
+					 rc);
 	}
 }
 
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index ef0e329..a507695 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -463,6 +463,9 @@ lpfc_config_port_post(struct lpfc_hba *phba)
 	if (phba->sli_rev != 3)
 		lpfc_post_rcv_buf(phba);
 
+	/* Initialize ERATT handling flag */
+	phba->hba_flag &= ~HBA_ERATT_HANDLED;
+
 	/* Enable appropriate host interrupts */
 	spin_lock_irq(&phba->hbalock);
 	status = readl(phba->HCregaddr);
@@ -505,6 +508,9 @@ lpfc_config_port_post(struct lpfc_hba *phba)
 		}
 	}
 
+	/* Set up error attention (ERATT) polling timer */
+	mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
+
 	lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed);
 	pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
 	lpfc_set_loopback_flag(phba);
@@ -816,6 +822,63 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
 	return;
 }
 
+static void
+lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
+{
+	uint32_t old_host_status = phba->work_hs;
+	struct lpfc_sli_ring  *pring;
+	struct lpfc_sli *psli = &phba->sli;
+
+	lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+		"0473 Deferred Adapter Hardware Error "
+		"Data: x%x x%x x%x\n",
+		phba->work_hs,
+		phba->work_status[0], phba->work_status[1]);
+
+	spin_lock_irq(&phba->hbalock);
+	psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+	spin_unlock_irq(&phba->hbalock);
+
+
+	/*
+	 * Firmware stops when it triggred erratt. That could cause the I/Os
+	 * dropped by the firmware. Error iocb (I/O) on txcmplq and let the
+	 * SCSI layer retry it after re-establishing link.
+	 */
+	pring = &psli->ring[psli->fcp_ring];
+	lpfc_sli_abort_iocb_ring(phba, pring);
+
+	/*
+	 * There was a firmware error. Take the hba offline and then
+	 * attempt to restart it.
+	 */
+	lpfc_offline_prep(phba);
+	lpfc_offline(phba);
+
+	/* Wait for the ER1 bit to clear.*/
+	while (phba->work_hs & HS_FFER1) {
+		msleep(100);
+		phba->work_hs = readl(phba->HSregaddr);
+		/* If driver is unloading let the worker thread continue */
+		if (phba->pport->load_flag & FC_UNLOADING) {
+			phba->work_hs = 0;
+			break;
+		}
+	}
+
+	/*
+	 * This is to ptrotect against a race condition in which
+	 * first write to the host attention register clear the
+	 * host status register.
+	 */
+	if ((!phba->work_hs) && (!(phba->pport->load_flag & FC_UNLOADING)))
+		phba->work_hs = old_host_status & ~HS_FFER1;
+
+	phba->hba_flag &= ~DEFER_ERATT;
+	phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
+	phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
+}
+
 /************************************************************************/
 /*                                                                      */
 /*    lpfc_handle_eratt                                                 */
@@ -854,6 +917,9 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
 				  (char *) &board_event,
 				  LPFC_NL_VENDOR_ID);
 
+	if (phba->hba_flag & DEFER_ERATT)
+		lpfc_handle_deferred_eratt(phba);
+
 	if (phba->work_hs & HS_FFER6) {
 		/* Re-establishing Link */
 		lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
@@ -1260,7 +1326,8 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
 		m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
 		break;
 	case PCI_DEVICE_ID_ZEPHYR_DCSP:
-		m = (typeof(m)){"LPe11002-SP", max_speed, "PCIe"};
+		m = (typeof(m)){"LP2105", max_speed, "PCIe"};
+		GE = 1;
 		break;
 	case PCI_DEVICE_ID_ZMID:
 		m = (typeof(m)){"LPe1150", max_speed, "PCIe"};
@@ -1658,6 +1725,7 @@ lpfc_stop_phba_timers(struct lpfc_hba *phba)
 	del_timer_sync(&phba->fabric_block_timer);
 	phba->hb_outstanding = 0;
 	del_timer_sync(&phba->hb_tmofunc);
+	del_timer_sync(&phba->eratt_poll);
 	return;
 }
 
@@ -2200,6 +2268,11 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
 	init_timer(&phba->fabric_block_timer);
 	phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
 	phba->fabric_block_timer.data = (unsigned long) phba;
+	/* EA polling mode timer */
+	init_timer(&phba->eratt_poll);
+	phba->eratt_poll.function = lpfc_poll_eratt;
+	phba->eratt_poll.data = (unsigned long) phba;
+
 
 	pci_set_master(pdev);
 	retval = pci_set_mwi(pdev);
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 8f548ad..3016c8d 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -493,6 +493,7 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
 	      struct lpfc_iocbq *cmdiocb, uint32_t els_cmd)
 {
 	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+	struct ls_rjt stat;
 
 	/* Put ndlp in NPR state with 1 sec timeout for plogi, ACC logo */
 	/* Only call LOGO ACC for first LOGO, this avoids sending unnecessary
@@ -503,8 +504,24 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
 	spin_unlock_irq(shost->host_lock);
 	if (els_cmd == ELS_CMD_PRLO)
 		lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL);
-	else
-		lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+	else {
+		/*
+		 * Send a reject for a LOGO from a Fabric Nport.  This
+		 * will cause a link bounce and force re-discovery of
+		 * the targets.
+		 */
+		if ((ndlp->nlp_type & NLP_FABRIC) &&
+			(ndlp->nlp_DID == Fabric_DID)) {
+			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);
+		} else
+			lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb,
+				ndlp, NULL);
+	}
 
 	if ((!(ndlp->nlp_type & NLP_FABRIC) &&
 	     ((ndlp->nlp_type & NLP_FCP_TARGET) ||
@@ -658,11 +675,27 @@ lpfc_rcv_logo_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
 {
 	struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
 	struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
+	struct ls_rjt stat;
 
 	spin_lock_irq(shost->host_lock);
 	ndlp->nlp_flag |= NLP_LOGO_ACC;
 	spin_unlock_irq(shost->host_lock);
-	lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+
+	/*
+	 * Send a reject for a LOGO from a Fabric Nport.  This
+	 * will cause a link bounce and force re-discovery of
+	 * the targets.
+	 */
+	if ((ndlp->nlp_type & NLP_FABRIC) &&
+		(ndlp->nlp_DID == Fabric_DID)) {
+		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);
+	} else
+		lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
 
 	return ndlp->nlp_state;
 }
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index eeeb142..546db87 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1288,6 +1288,107 @@ lpfc_sli_rsp_pointers_error(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
 	return;
 }
 
+/**
+ * lpfc_sli_check_eratt - check error attention events
+ * @phba: Pointer to HBA context.
+ *
+ * This function is called form timer soft interrupt context to check HBA's
+ * error attention register bit for error attention events.
+ *
+ * This fucntion returns 1 when there is Error Attention in the Host Attention
+ * Register and returns 0 otherwise.
+ **/
+int
+lpfc_sli_check_eratt(struct lpfc_hba *phba)
+{
+	uint32_t ha_copy;
+
+	/* If PCI channel is offline, don't process it */
+	if (unlikely(phba->pcidev->error_state != pci_channel_io_normal))
+		return 0;
+
+	/* If somebody is waiting to handle an eratt, don't process it
+	 * here. The brdkill function will do this.
+	 */
+	if (phba->link_flag & LS_IGNORE_ERATT)
+		return 0;
+
+	/* Check if interrupt handler handles this ERATT */
+	spin_lock_irq(&phba->hbalock);
+	if (phba->hba_flag & HBA_ERATT_HANDLED) {
+		/* Interrupt handler has handled ERATT */
+		spin_unlock_irq(&phba->hbalock);
+		return 0;
+	}
+
+	/*
+	 * If there is deferred error attention, do not check for error
+	 * attention
+	 */
+	if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+		spin_unlock_irq(&phba->hbalock);
+		return 0;
+	}
+
+
+	/* Read chip Host Attention (HA) register */
+	ha_copy = readl(phba->HAregaddr);
+
+	if (ha_copy & HA_ERATT) {
+		/* Read host status register to retrieve error event */
+		lpfc_sli_read_hs(phba);
+
+		/* Check if there is a deferred error condition is active */
+		if ((HS_FFER1 & phba->work_hs) &&
+			((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
+			HS_FFER6 | HS_FFER7) & phba->work_hs)) {
+			phba->hba_flag |= DEFER_ERATT;
+			/* Clear all interrupt enable conditions */
+			writel(0, phba->HCregaddr);
+			readl(phba->HCregaddr);
+		}
+		/* Set the driver HA work bitmap */
+		phba->work_ha |= HA_ERATT;
+		/* Indicate polling handles this ERATT */
+		phba->hba_flag |= HBA_ERATT_HANDLED;
+		spin_unlock_irq(&phba->hbalock);
+		return 1;
+	}
+	spin_unlock_irq(&phba->hbalock);
+	return 0;
+}
+
+/**
+ * lpfc_poll_eratt - Error attention polling timer timeout handler
+ * @ptr: Pointer to address of HBA context object.
+ *
+ * This function is invoked by the Error Attention polling timer when the
+ * timer times out. It will check the SLI Error Attention register for
+ * possible attention events. If so, it will post an Error Attention event
+ * and wake up worker thread to process it. Otherwise, it will set up the
+ * Error Attention polling timer for the next poll.
+ **/
+void lpfc_poll_eratt(unsigned long ptr)
+{
+	struct lpfc_hba *phba;
+	uint32_t eratt = 0;
+
+	phba = (struct lpfc_hba *)ptr;
+
+	/* Check chip HA register for error event */
+	eratt = lpfc_sli_check_eratt(phba);
+
+	if (eratt)
+		/* Tell the worker thread there is work to do */
+		lpfc_worker_wake_up(phba);
+	/* If the error attention is already handled do not poll */
+	else if (!(phba->hba_flag & HBA_ERATT_HANDLED))
+		/* Restart the timer for next eratt poll */
+		mod_timer(&phba->eratt_poll, jiffies +
+					HZ * LPFC_ERATT_POLL_INTERVAL);
+	return;
+}
+
 void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
 {
 	struct lpfc_sli      *psli  = &phba->sli;
@@ -2823,6 +2924,12 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
 		goto out_not_finished;
 	}
 
+	/* If HBA has a deferred error attention, fail the iocb. */
+	if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+		spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
+		goto out_not_finished;
+	}
+
 	psli = &phba->sli;
 
 	mb = &pmbox->mb;
@@ -3178,6 +3285,10 @@ __lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 	if (unlikely(phba->pcidev->error_state != pci_channel_io_normal))
 		return IOCB_ERROR;
 
+	/* If HBA has a deferred error attention, fail the iocb. */
+	if (unlikely(phba->hba_flag & DEFER_ERATT))
+		return IOCB_ERROR;
+
 	/*
 	 * We should never get an IOCB if we are in a < LINK_DOWN state
 	 */
@@ -3320,6 +3431,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
 	uint16_t temp;
 	struct temp_event temp_event_data;
 	struct Scsi_Host *shost;
+	uint32_t *iocb_w;
 
 	icmd = &iocbq->iocb;
 	evt_code = icmd->un.asyncstat.evt_code;
@@ -3327,13 +3439,23 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
 
 	if ((evt_code != ASYNC_TEMP_WARN) &&
 		(evt_code != ASYNC_TEMP_SAFE)) {
+		iocb_w = (uint32_t *) icmd;
 		lpfc_printf_log(phba,
 			KERN_ERR,
 			LOG_SLI,
 			"0346 Ring %d handler: unexpected ASYNC_STATUS"
-			" evt_code 0x%x\n",
+			" evt_code 0x%x \n"
+			"W0  0x%08x W1  0x%08x W2  0x%08x W3  0x%08x\n"
+			"W4  0x%08x W5  0x%08x W6  0x%08x W7  0x%08x\n"
+			"W8  0x%08x W9  0x%08x W10 0x%08x W11 0x%08x\n"
+			"W12 0x%08x W13 0x%08x W14 0x%08x W15 0x%08x\n",
 			pring->ringno,
-			icmd->un.asyncstat.evt_code);
+			icmd->un.asyncstat.evt_code,
+			iocb_w[0], iocb_w[1], iocb_w[2], iocb_w[3],
+			iocb_w[4], iocb_w[5], iocb_w[6], iocb_w[7],
+			iocb_w[8], iocb_w[9], iocb_w[10], iocb_w[11],
+			iocb_w[12], iocb_w[13], iocb_w[14], iocb_w[15]);
+
 		return;
 	}
 	temp_event_data.data = (uint32_t)temp;
@@ -4274,6 +4396,25 @@ lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs)
 	 */
 	if (phba->link_flag & LS_IGNORE_ERATT)
 		ha_copy &= ~HA_ERATT;
+
+	/* Check the need for handling ERATT in interrupt handler */
+	if (ha_copy & HA_ERATT) {
+		if (phba->hba_flag & HBA_ERATT_HANDLED)
+			/* ERATT polling has handled ERATT */
+			ha_copy &= ~HA_ERATT;
+		else
+			/* Indicate interrupt handler handles ERATT */
+			phba->hba_flag |= HBA_ERATT_HANDLED;
+	}
+
+	/*
+	 * If there is deferred error attention, do not check for any interrupt.
+	 */
+	if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+		spin_unlock_irq(&phba->hbalock);
+		return IRQ_NONE;
+	}
+
 	writel((ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
 	readl(phba->HAregaddr); /* flush */
 	spin_unlock(&phba->hbalock);
@@ -4344,25 +4485,20 @@ lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs)
 			}
 		}
 
+		spin_lock(&phba->hbalock);
 		if (work_ha_copy & HA_ERATT) {
-			/*
-			 * There was a link/board error.  Read the
-			 * status register to retrieve the error event
-			 * and process it.
-			 */
-			phba->sli.slistat.err_attn_event++;
-			/* Save status info */
-			phba->work_hs = readl(phba->HSregaddr);
-			phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
-			phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
-
-			/* Clear Chip error bit */
-			writel(HA_ERATT, phba->HAregaddr);
-			readl(phba->HAregaddr); /* flush */
-			phba->pport->stopped = 1;
+			lpfc_sli_read_hs(phba);
+			/* Check for an active deferred error condition */
+			if ((HS_FFER1 & phba->work_hs) &&
+				((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
+				 HS_FFER6 | HS_FFER7) & phba->work_hs)) {
+					phba->hba_flag |= DEFER_ERATT;
+					/* Clear all interrupt conditions */
+					writel(0, phba->HCregaddr);
+					readl(phba->HCregaddr);
+			}
 		}
 
-		spin_lock(&phba->hbalock);
 		if ((work_ha_copy & HA_MBATT) &&
 		    (phba->sli.mbox_active)) {
 			pmb = phba->sli.mbox_active;
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index f8b9a23..f1e501d 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "8.2.0.39"
+#define LPFC_DRIVER_VERSION "8.2.0.40"
 
 #define LPFC_DRIVER_NAME "lpfc"