diff options
-rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 10 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 6 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 97 |
3 files changed, 91 insertions, 22 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index cbe07d7..10f983b 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -392,6 +392,13 @@ enum hba_temp_state { HBA_OVER_TEMP }; +enum intr_type_t { + NONE = 0, + INTx, + MSI, + MSIX, +}; + struct lpfc_hba { struct lpfc_sli sli; uint32_t sli_rev; /* SLI2 or SLI3 */ @@ -555,7 +562,8 @@ struct lpfc_hba { mempool_t *nlp_mem_pool; struct fc_host_statistics link_stats; - uint8_t using_msi; + enum intr_type_t intr_type; + struct msix_entry msix_entries[1]; struct list_head port_list; struct lpfc_vport *pport; /* physical lpfc_vport pointer */ diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index fc48e40..b12a841 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1592,9 +1592,11 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255, # support this feature # 0 = MSI disabled (default) # 1 = MSI enabled -# Value range is [0,1]. Default value is 0. +# 2 = MSI-X enabled +# Value range is [0,2]. Default value is 0. */ -LPFC_ATTR_R(use_msi, 0, 0, 1, "Use Message Signaled Interrupts, if possible"); +LPFC_ATTR_R(use_msi, 0, 0, 2, "Use Message Signaled Interrupts (1) or " + "MSI-X (2), if possible"); /* # lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware. diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 9914154..a087524 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1924,6 +1924,42 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost) spin_unlock_irq(shost->host_lock); } +static int +lpfc_enable_msix(struct lpfc_hba *phba) +{ + int error; + + phba->msix_entries[0].entry = 0; + phba->msix_entries[0].vector = 0; + + error = pci_enable_msix(phba->pcidev, phba->msix_entries, + ARRAY_SIZE(phba->msix_entries)); + if (error) { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0420 Enable MSI-X failed (%d), continuing " + "with MSI\n", error); + pci_disable_msix(phba->pcidev); + return error; + } + + error = request_irq(phba->msix_entries[0].vector, lpfc_intr_handler, 0, + LPFC_DRIVER_NAME, phba); + if (error) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0421 MSI-X request_irq failed (%d), " + "continuing with MSI\n", error); + pci_disable_msix(phba->pcidev); + } + return error; +} + +static void +lpfc_disable_msix(struct lpfc_hba *phba) +{ + free_irq(phba->msix_entries[0].vector, phba); + pci_disable_msix(phba->pcidev); +} + static int __devinit lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) { @@ -2125,24 +2161,36 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) lpfc_debugfs_initialize(vport); pci_set_drvdata(pdev, shost); + phba->intr_type = NONE; - if (phba->cfg_use_msi) { + if (phba->cfg_use_msi == 2) { + error = lpfc_enable_msix(phba); + if (!error) + phba->intr_type = MSIX; + } + + /* Fallback to MSI if MSI-X initialization failed */ + if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) { retval = pci_enable_msi(phba->pcidev); if (!retval) - phba->using_msi = 1; + phba->intr_type = MSI; else lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "0452 Enable MSI failed, continuing " "with IRQ\n"); } - retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, IRQF_SHARED, - LPFC_DRIVER_NAME, phba); - if (retval) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0451 Enable interrupt handler failed\n"); - error = retval; - goto out_disable_msi; + /* MSI-X is the only case the doesn't need to call request_irq */ + if (phba->intr_type != MSIX) { + retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, + IRQF_SHARED, LPFC_DRIVER_NAME, phba); + if (retval) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable " + "interrupt handler failed\n"); + error = retval; + goto out_disable_msi; + } else if (phba->intr_type != MSI) + phba->intr_type = INTx; } phba->MBslimaddr = phba->slim_memmap_p; @@ -2187,9 +2235,14 @@ out_remove_device: out_free_irq: lpfc_stop_phba_timers(phba); phba->pport->work_port_events = 0; - free_irq(phba->pcidev->irq, phba); + + if (phba->intr_type == MSIX) + lpfc_disable_msix(phba); + else + free_irq(phba->pcidev->irq, phba); + out_disable_msi: - if (phba->using_msi) + if (phba->intr_type == MSI) pci_disable_msi(phba->pcidev); destroy_port(vport); out_kthread_stop: @@ -2262,10 +2315,13 @@ lpfc_pci_remove_one(struct pci_dev *pdev) lpfc_debugfs_terminate(vport); - /* Release the irq reservation */ - free_irq(phba->pcidev->irq, phba); - if (phba->using_msi) - pci_disable_msi(phba->pcidev); + if (phba->intr_type == MSIX) + lpfc_disable_msix(phba); + else { + free_irq(phba->pcidev->irq, phba); + if (phba->intr_type == MSI) + pci_disable_msi(phba->pcidev); + } pci_set_drvdata(pdev, NULL); scsi_host_put(shost); @@ -2324,10 +2380,13 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev, pring = &psli->ring[psli->fcp_ring]; lpfc_sli_abort_iocb_ring(phba, pring); - /* Release the irq reservation */ - free_irq(phba->pcidev->irq, phba); - if (phba->using_msi) - pci_disable_msi(phba->pcidev); + if (phba->intr_type == MSIX) + lpfc_disable_msix(phba); + else { + free_irq(phba->pcidev->irq, phba); + if (phba->intr_type == MSI) + pci_disable_msi(phba->pcidev); + } /* Request a slot reset. */ return PCI_ERS_RESULT_NEED_RESET; |