|
@@ -7030,22 +7030,28 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba)
|
|
|
static int
|
|
|
lpfc_sli4_enable_msix(struct lpfc_hba *phba)
|
|
|
{
|
|
|
- int rc, index;
|
|
|
+ int vectors, rc, index;
|
|
|
|
|
|
/* Set up MSI-X multi-message vectors */
|
|
|
for (index = 0; index < phba->sli4_hba.cfg_eqn; index++)
|
|
|
phba->sli4_hba.msix_entries[index].entry = index;
|
|
|
|
|
|
/* Configure MSI-X capability structure */
|
|
|
+ vectors = phba->sli4_hba.cfg_eqn;
|
|
|
+enable_msix_vectors:
|
|
|
rc = pci_enable_msix(phba->pcidev, phba->sli4_hba.msix_entries,
|
|
|
- phba->sli4_hba.cfg_eqn);
|
|
|
- if (rc) {
|
|
|
+ vectors);
|
|
|
+ if (rc > 1) {
|
|
|
+ vectors = rc;
|
|
|
+ goto enable_msix_vectors;
|
|
|
+ } else if (rc) {
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
|
|
"0484 PCI enable MSI-X failed (%d)\n", rc);
|
|
|
goto msi_fail_out;
|
|
|
}
|
|
|
+
|
|
|
/* Log MSI-X vector assignment */
|
|
|
- for (index = 0; index < phba->sli4_hba.cfg_eqn; index++)
|
|
|
+ for (index = 0; index < vectors; index++)
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
|
|
"0489 MSI-X entry[%d]: vector=x%x "
|
|
|
"message=%d\n", index,
|
|
@@ -7067,7 +7073,7 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
|
|
|
}
|
|
|
|
|
|
/* The rest of the vector(s) are associated to fast-path handler(s) */
|
|
|
- for (index = 1; index < phba->sli4_hba.cfg_eqn; index++) {
|
|
|
+ for (index = 1; index < vectors; index++) {
|
|
|
phba->sli4_hba.fcp_eq_hdl[index - 1].idx = index - 1;
|
|
|
phba->sli4_hba.fcp_eq_hdl[index - 1].phba = phba;
|
|
|
rc = request_irq(phba->sli4_hba.msix_entries[index].vector,
|
|
@@ -7081,6 +7087,7 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
|
|
|
goto cfg_fail_out;
|
|
|
}
|
|
|
}
|
|
|
+ phba->sli4_hba.msix_vec_nr = vectors;
|
|
|
|
|
|
return rc;
|
|
|
|
|
@@ -7114,9 +7121,10 @@ lpfc_sli4_disable_msix(struct lpfc_hba *phba)
|
|
|
/* Free up MSI-X multi-message vectors */
|
|
|
free_irq(phba->sli4_hba.msix_entries[0].vector, phba);
|
|
|
|
|
|
- for (index = 1; index < phba->sli4_hba.cfg_eqn; index++)
|
|
|
+ for (index = 1; index < phba->sli4_hba.msix_vec_nr; index++)
|
|
|
free_irq(phba->sli4_hba.msix_entries[index].vector,
|
|
|
&phba->sli4_hba.fcp_eq_hdl[index - 1]);
|
|
|
+
|
|
|
/* Disable MSI-X */
|
|
|
pci_disable_msix(phba->pcidev);
|
|
|
|
|
@@ -7158,6 +7166,7 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba)
|
|
|
pci_disable_msi(phba->pcidev);
|
|
|
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
|
|
|
"0490 MSI request_irq failed (%d)\n", rc);
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
for (index = 0; index < phba->cfg_fcp_eq_count; index++) {
|
|
@@ -7165,7 +7174,7 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba)
|
|
|
phba->sli4_hba.fcp_eq_hdl[index].phba = phba;
|
|
|
}
|
|
|
|
|
|
- return rc;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -7876,6 +7885,9 @@ lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
"2710 PCI channel disable preparing for reset\n");
|
|
|
|
|
|
+ /* Block any management I/Os to the device */
|
|
|
+ lpfc_block_mgmt_io(phba);
|
|
|
+
|
|
|
/* Block all SCSI devices' I/Os on the host */
|
|
|
lpfc_scsi_dev_block(phba);
|
|
|
|
|
@@ -7885,6 +7897,7 @@ lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
|
|
|
/* Disable interrupt and pci device */
|
|
|
lpfc_sli_disable_intr(phba);
|
|
|
pci_disable_device(phba->pcidev);
|
|
|
+
|
|
|
/* Flush all driver's outstanding SCSI I/Os as we are to reset */
|
|
|
lpfc_sli_flush_fcp_rings(phba);
|
|
|
}
|
|
@@ -7898,7 +7911,7 @@ lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
|
|
|
* pending I/Os.
|
|
|
**/
|
|
|
static void
|
|
|
-lpfc_prep_dev_for_perm_failure(struct lpfc_hba *phba)
|
|
|
+lpfc_sli_prep_dev_for_perm_failure(struct lpfc_hba *phba)
|
|
|
{
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
"2711 PCI channel permanent disable for failure\n");
|
|
@@ -7947,7 +7960,7 @@ lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state)
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
case pci_channel_io_perm_failure:
|
|
|
/* Permanent failure, prepare for device down */
|
|
|
- lpfc_prep_dev_for_perm_failure(phba);
|
|
|
+ lpfc_sli_prep_dev_for_perm_failure(phba);
|
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
|
default:
|
|
|
/* Unknown state, prepare and request slot reset */
|
|
@@ -8016,7 +8029,8 @@ lpfc_io_slot_reset_s3(struct pci_dev *pdev)
|
|
|
} else
|
|
|
phba->intr_mode = intr_mode;
|
|
|
|
|
|
- /* Take device offline; this will perform cleanup */
|
|
|
+ /* Take device offline, it will perform cleanup */
|
|
|
+ lpfc_offline_prep(phba);
|
|
|
lpfc_offline(phba);
|
|
|
lpfc_sli_brdrestart(phba);
|
|
|
|
|
@@ -8201,6 +8215,8 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
|
|
|
/* Default to single FCP EQ for non-MSI-X */
|
|
|
if (phba->intr_type != MSIX)
|
|
|
phba->cfg_fcp_eq_count = 1;
|
|
|
+ else if (phba->sli4_hba.msix_vec_nr < phba->cfg_fcp_eq_count)
|
|
|
+ phba->cfg_fcp_eq_count = phba->sli4_hba.msix_vec_nr - 1;
|
|
|
/* Set up SLI-4 HBA */
|
|
|
if (lpfc_sli4_hba_setup(phba)) {
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
@@ -8362,7 +8378,7 @@ lpfc_pci_suspend_one_s4(struct pci_dev *pdev, pm_message_t msg)
|
|
|
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
|
|
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
|
|
- "0298 PCI device Power Management suspend.\n");
|
|
|
+ "2843 PCI device Power Management suspend.\n");
|
|
|
|
|
|
/* Bring down the device */
|
|
|
lpfc_offline_prep(phba);
|
|
@@ -8452,6 +8468,84 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * lpfc_sli4_prep_dev_for_recover - Prepare SLI4 device for pci slot recover
|
|
|
+ * @phba: pointer to lpfc hba data structure.
|
|
|
+ *
|
|
|
+ * This routine is called to prepare the SLI4 device for PCI slot recover. It
|
|
|
+ * aborts all the outstanding SCSI I/Os to the pci device.
|
|
|
+ **/
|
|
|
+static void
|
|
|
+lpfc_sli4_prep_dev_for_recover(struct lpfc_hba *phba)
|
|
|
+{
|
|
|
+ struct lpfc_sli *psli = &phba->sli;
|
|
|
+ struct lpfc_sli_ring *pring;
|
|
|
+
|
|
|
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
+ "2828 PCI channel I/O abort preparing for recovery\n");
|
|
|
+ /*
|
|
|
+ * There may be errored I/Os through HBA, abort all I/Os on txcmplq
|
|
|
+ * and let the SCSI mid-layer to retry them to recover.
|
|
|
+ */
|
|
|
+ pring = &psli->ring[psli->fcp_ring];
|
|
|
+ lpfc_sli_abort_iocb_ring(phba, pring);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_sli4_prep_dev_for_reset - Prepare SLI4 device for pci slot reset
|
|
|
+ * @phba: pointer to lpfc hba data structure.
|
|
|
+ *
|
|
|
+ * This routine is called to prepare the SLI4 device for PCI slot reset. It
|
|
|
+ * disables the device interrupt and pci device, and aborts the internal FCP
|
|
|
+ * pending I/Os.
|
|
|
+ **/
|
|
|
+static void
|
|
|
+lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba)
|
|
|
+{
|
|
|
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
+ "2826 PCI channel disable preparing for reset\n");
|
|
|
+
|
|
|
+ /* Block any management I/Os to the device */
|
|
|
+ lpfc_block_mgmt_io(phba);
|
|
|
+
|
|
|
+ /* Block all SCSI devices' I/Os on the host */
|
|
|
+ lpfc_scsi_dev_block(phba);
|
|
|
+
|
|
|
+ /* stop all timers */
|
|
|
+ lpfc_stop_hba_timers(phba);
|
|
|
+
|
|
|
+ /* Disable interrupt and pci device */
|
|
|
+ lpfc_sli4_disable_intr(phba);
|
|
|
+ pci_disable_device(phba->pcidev);
|
|
|
+
|
|
|
+ /* Flush all driver's outstanding SCSI I/Os as we are to reset */
|
|
|
+ lpfc_sli_flush_fcp_rings(phba);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_sli4_prep_dev_for_perm_failure - Prepare SLI4 dev for pci slot disable
|
|
|
+ * @phba: pointer to lpfc hba data structure.
|
|
|
+ *
|
|
|
+ * This routine is called to prepare the SLI4 device for PCI slot permanently
|
|
|
+ * disabling. It blocks the SCSI transport layer traffic and flushes the FCP
|
|
|
+ * pending I/Os.
|
|
|
+ **/
|
|
|
+static void
|
|
|
+lpfc_sli4_prep_dev_for_perm_failure(struct lpfc_hba *phba)
|
|
|
+{
|
|
|
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
+ "2827 PCI channel permanent disable for failure\n");
|
|
|
+
|
|
|
+ /* Block all SCSI devices' I/Os on the host */
|
|
|
+ lpfc_scsi_dev_block(phba);
|
|
|
+
|
|
|
+ /* stop all timers */
|
|
|
+ lpfc_stop_hba_timers(phba);
|
|
|
+
|
|
|
+ /* Clean up all driver's outstanding SCSI I/Os */
|
|
|
+ lpfc_sli_flush_fcp_rings(phba);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* lpfc_io_error_detected_s4 - Method for handling PCI I/O error to SLI-4 device
|
|
|
* @pdev: pointer to PCI device.
|
|
@@ -8471,7 +8565,29 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev)
|
|
|
static pci_ers_result_t
|
|
|
lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state)
|
|
|
{
|
|
|
- return PCI_ERS_RESULT_NEED_RESET;
|
|
|
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
|
|
+
|
|
|
+ switch (state) {
|
|
|
+ case pci_channel_io_normal:
|
|
|
+ /* Non-fatal error, prepare for recovery */
|
|
|
+ lpfc_sli4_prep_dev_for_recover(phba);
|
|
|
+ return PCI_ERS_RESULT_CAN_RECOVER;
|
|
|
+ case pci_channel_io_frozen:
|
|
|
+ /* Fatal error, prepare for slot reset */
|
|
|
+ lpfc_sli4_prep_dev_for_reset(phba);
|
|
|
+ return PCI_ERS_RESULT_NEED_RESET;
|
|
|
+ case pci_channel_io_perm_failure:
|
|
|
+ /* Permanent failure, prepare for device down */
|
|
|
+ lpfc_sli4_prep_dev_for_perm_failure(phba);
|
|
|
+ return PCI_ERS_RESULT_DISCONNECT;
|
|
|
+ default:
|
|
|
+ /* Unknown state, prepare and request slot reset */
|
|
|
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
+ "2825 Unknown PCI error state: x%x\n", state);
|
|
|
+ lpfc_sli4_prep_dev_for_reset(phba);
|
|
|
+ return PCI_ERS_RESULT_NEED_RESET;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -8495,6 +8611,39 @@ lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state)
|
|
|
static pci_ers_result_t
|
|
|
lpfc_io_slot_reset_s4(struct pci_dev *pdev)
|
|
|
{
|
|
|
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
|
|
+ struct lpfc_sli *psli = &phba->sli;
|
|
|
+ uint32_t intr_mode;
|
|
|
+
|
|
|
+ dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
|
|
|
+ if (pci_enable_device_mem(pdev)) {
|
|
|
+ printk(KERN_ERR "lpfc: Cannot re-enable "
|
|
|
+ "PCI device after reset.\n");
|
|
|
+ return PCI_ERS_RESULT_DISCONNECT;
|
|
|
+ }
|
|
|
+
|
|
|
+ pci_restore_state(pdev);
|
|
|
+ if (pdev->is_busmaster)
|
|
|
+ pci_set_master(pdev);
|
|
|
+
|
|
|
+ spin_lock_irq(&phba->hbalock);
|
|
|
+ psli->sli_flag &= ~LPFC_SLI_ACTIVE;
|
|
|
+ spin_unlock_irq(&phba->hbalock);
|
|
|
+
|
|
|
+ /* Configure and enable interrupt */
|
|
|
+ intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
|
|
|
+ if (intr_mode == LPFC_INTR_ERROR) {
|
|
|
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
+ "2824 Cannot re-enable interrupt after "
|
|
|
+ "slot reset.\n");
|
|
|
+ return PCI_ERS_RESULT_DISCONNECT;
|
|
|
+ } else
|
|
|
+ phba->intr_mode = intr_mode;
|
|
|
+
|
|
|
+ /* Log the current active interrupt mode */
|
|
|
+ lpfc_log_intr_mode(phba, phba->intr_mode);
|
|
|
+
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
|
}
|
|
|
|
|
@@ -8511,7 +8660,27 @@ lpfc_io_slot_reset_s4(struct pci_dev *pdev)
|
|
|
static void
|
|
|
lpfc_io_resume_s4(struct pci_dev *pdev)
|
|
|
{
|
|
|
- return;
|
|
|
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * In case of slot reset, as function reset is performed through
|
|
|
+ * mailbox command which needs DMA to be enabled, this operation
|
|
|
+ * has to be moved to the io resume phase. Taking device offline
|
|
|
+ * will perform the necessary cleanup.
|
|
|
+ */
|
|
|
+ if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) {
|
|
|
+ /* Perform device reset */
|
|
|
+ lpfc_offline_prep(phba);
|
|
|
+ lpfc_offline(phba);
|
|
|
+ lpfc_sli_brdrestart(phba);
|
|
|
+ /* Bring the device back online */
|
|
|
+ lpfc_online(phba);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Clean up Advanced Error Reporting (AER) if needed */
|
|
|
+ if (phba->hba_flag & HBA_AER_ENABLED)
|
|
|
+ pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
|
}
|
|
|
|
|
|
/**
|