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 cbe07d79bd12..10f983b479c6 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h | |||
| @@ -392,6 +392,13 @@ enum hba_temp_state { | |||
| 392 | HBA_OVER_TEMP | 392 | HBA_OVER_TEMP |
| 393 | }; | 393 | }; |
| 394 | 394 | ||
| 395 | enum intr_type_t { | ||
| 396 | NONE = 0, | ||
| 397 | INTx, | ||
| 398 | MSI, | ||
| 399 | MSIX, | ||
| 400 | }; | ||
| 401 | |||
| 395 | struct lpfc_hba { | 402 | struct lpfc_hba { |
| 396 | struct lpfc_sli sli; | 403 | struct lpfc_sli sli; |
| 397 | uint32_t sli_rev; /* SLI2 or SLI3 */ | 404 | uint32_t sli_rev; /* SLI2 or SLI3 */ |
| @@ -555,7 +562,8 @@ struct lpfc_hba { | |||
| 555 | mempool_t *nlp_mem_pool; | 562 | mempool_t *nlp_mem_pool; |
| 556 | 563 | ||
| 557 | struct fc_host_statistics link_stats; | 564 | struct fc_host_statistics link_stats; |
| 558 | uint8_t using_msi; | 565 | enum intr_type_t intr_type; |
| 566 | struct msix_entry msix_entries[1]; | ||
| 559 | 567 | ||
| 560 | struct list_head port_list; | 568 | struct list_head port_list; |
| 561 | struct lpfc_vport *pport; /* physical lpfc_vport pointer */ | 569 | 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 fc48e40c4d29..b12a841703ca 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, | |||
| 1592 | # support this feature | 1592 | # support this feature |
| 1593 | # 0 = MSI disabled (default) | 1593 | # 0 = MSI disabled (default) |
| 1594 | # 1 = MSI enabled | 1594 | # 1 = MSI enabled |
| 1595 | # Value range is [0,1]. Default value is 0. | 1595 | # 2 = MSI-X enabled |
| 1596 | # Value range is [0,2]. Default value is 0. | ||
| 1596 | */ | 1597 | */ |
| 1597 | LPFC_ATTR_R(use_msi, 0, 0, 1, "Use Message Signaled Interrupts, if possible"); | 1598 | LPFC_ATTR_R(use_msi, 0, 0, 2, "Use Message Signaled Interrupts (1) or " |
| 1599 | "MSI-X (2), if possible"); | ||
| 1598 | 1600 | ||
| 1599 | /* | 1601 | /* |
| 1600 | # lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware. | 1602 | # 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 99141545c25e..a087524acf41 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) | |||
| 1924 | spin_unlock_irq(shost->host_lock); | 1924 | spin_unlock_irq(shost->host_lock); |
| 1925 | } | 1925 | } |
| 1926 | 1926 | ||
| 1927 | static int | ||
| 1928 | lpfc_enable_msix(struct lpfc_hba *phba) | ||
| 1929 | { | ||
| 1930 | int error; | ||
| 1931 | |||
| 1932 | phba->msix_entries[0].entry = 0; | ||
| 1933 | phba->msix_entries[0].vector = 0; | ||
| 1934 | |||
| 1935 | error = pci_enable_msix(phba->pcidev, phba->msix_entries, | ||
| 1936 | ARRAY_SIZE(phba->msix_entries)); | ||
| 1937 | if (error) { | ||
| 1938 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, | ||
| 1939 | "0420 Enable MSI-X failed (%d), continuing " | ||
| 1940 | "with MSI\n", error); | ||
| 1941 | pci_disable_msix(phba->pcidev); | ||
| 1942 | return error; | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | error = request_irq(phba->msix_entries[0].vector, lpfc_intr_handler, 0, | ||
| 1946 | LPFC_DRIVER_NAME, phba); | ||
| 1947 | if (error) { | ||
| 1948 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
| 1949 | "0421 MSI-X request_irq failed (%d), " | ||
| 1950 | "continuing with MSI\n", error); | ||
| 1951 | pci_disable_msix(phba->pcidev); | ||
| 1952 | } | ||
| 1953 | return error; | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | static void | ||
| 1957 | lpfc_disable_msix(struct lpfc_hba *phba) | ||
| 1958 | { | ||
| 1959 | free_irq(phba->msix_entries[0].vector, phba); | ||
| 1960 | pci_disable_msix(phba->pcidev); | ||
| 1961 | } | ||
| 1962 | |||
| 1927 | static int __devinit | 1963 | static int __devinit |
| 1928 | lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | 1964 | lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) |
| 1929 | { | 1965 | { |
| @@ -2125,24 +2161,36 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | |||
| 2125 | lpfc_debugfs_initialize(vport); | 2161 | lpfc_debugfs_initialize(vport); |
| 2126 | 2162 | ||
| 2127 | pci_set_drvdata(pdev, shost); | 2163 | pci_set_drvdata(pdev, shost); |
| 2164 | phba->intr_type = NONE; | ||
| 2128 | 2165 | ||
| 2129 | if (phba->cfg_use_msi) { | 2166 | if (phba->cfg_use_msi == 2) { |
| 2167 | error = lpfc_enable_msix(phba); | ||
| 2168 | if (!error) | ||
| 2169 | phba->intr_type = MSIX; | ||
| 2170 | } | ||
| 2171 | |||
| 2172 | /* Fallback to MSI if MSI-X initialization failed */ | ||
| 2173 | if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) { | ||
| 2130 | retval = pci_enable_msi(phba->pcidev); | 2174 | retval = pci_enable_msi(phba->pcidev); |
| 2131 | if (!retval) | 2175 | if (!retval) |
| 2132 | phba->using_msi = 1; | 2176 | phba->intr_type = MSI; |
| 2133 | else | 2177 | else |
| 2134 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, | 2178 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, |
| 2135 | "0452 Enable MSI failed, continuing " | 2179 | "0452 Enable MSI failed, continuing " |
| 2136 | "with IRQ\n"); | 2180 | "with IRQ\n"); |
| 2137 | } | 2181 | } |
| 2138 | 2182 | ||
| 2139 | retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, IRQF_SHARED, | 2183 | /* MSI-X is the only case the doesn't need to call request_irq */ |
| 2140 | LPFC_DRIVER_NAME, phba); | 2184 | if (phba->intr_type != MSIX) { |
| 2141 | if (retval) { | 2185 | retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, |
| 2142 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | 2186 | IRQF_SHARED, LPFC_DRIVER_NAME, phba); |
| 2143 | "0451 Enable interrupt handler failed\n"); | 2187 | if (retval) { |
| 2144 | error = retval; | 2188 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable " |
| 2145 | goto out_disable_msi; | 2189 | "interrupt handler failed\n"); |
| 2190 | error = retval; | ||
| 2191 | goto out_disable_msi; | ||
| 2192 | } else if (phba->intr_type != MSI) | ||
| 2193 | phba->intr_type = INTx; | ||
| 2146 | } | 2194 | } |
| 2147 | 2195 | ||
| 2148 | phba->MBslimaddr = phba->slim_memmap_p; | 2196 | phba->MBslimaddr = phba->slim_memmap_p; |
| @@ -2187,9 +2235,14 @@ out_remove_device: | |||
| 2187 | out_free_irq: | 2235 | out_free_irq: |
| 2188 | lpfc_stop_phba_timers(phba); | 2236 | lpfc_stop_phba_timers(phba); |
| 2189 | phba->pport->work_port_events = 0; | 2237 | phba->pport->work_port_events = 0; |
| 2190 | free_irq(phba->pcidev->irq, phba); | 2238 | |
| 2239 | if (phba->intr_type == MSIX) | ||
| 2240 | lpfc_disable_msix(phba); | ||
| 2241 | else | ||
| 2242 | free_irq(phba->pcidev->irq, phba); | ||
| 2243 | |||
| 2191 | out_disable_msi: | 2244 | out_disable_msi: |
| 2192 | if (phba->using_msi) | 2245 | if (phba->intr_type == MSI) |
| 2193 | pci_disable_msi(phba->pcidev); | 2246 | pci_disable_msi(phba->pcidev); |
| 2194 | destroy_port(vport); | 2247 | destroy_port(vport); |
| 2195 | out_kthread_stop: | 2248 | out_kthread_stop: |
| @@ -2262,10 +2315,13 @@ lpfc_pci_remove_one(struct pci_dev *pdev) | |||
| 2262 | 2315 | ||
| 2263 | lpfc_debugfs_terminate(vport); | 2316 | lpfc_debugfs_terminate(vport); |
| 2264 | 2317 | ||
| 2265 | /* Release the irq reservation */ | 2318 | if (phba->intr_type == MSIX) |
| 2266 | free_irq(phba->pcidev->irq, phba); | 2319 | lpfc_disable_msix(phba); |
| 2267 | if (phba->using_msi) | 2320 | else { |
| 2268 | pci_disable_msi(phba->pcidev); | 2321 | free_irq(phba->pcidev->irq, phba); |
| 2322 | if (phba->intr_type == MSI) | ||
| 2323 | pci_disable_msi(phba->pcidev); | ||
| 2324 | } | ||
| 2269 | 2325 | ||
| 2270 | pci_set_drvdata(pdev, NULL); | 2326 | pci_set_drvdata(pdev, NULL); |
| 2271 | scsi_host_put(shost); | 2327 | scsi_host_put(shost); |
| @@ -2324,10 +2380,13 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev, | |||
| 2324 | pring = &psli->ring[psli->fcp_ring]; | 2380 | pring = &psli->ring[psli->fcp_ring]; |
| 2325 | lpfc_sli_abort_iocb_ring(phba, pring); | 2381 | lpfc_sli_abort_iocb_ring(phba, pring); |
| 2326 | 2382 | ||
| 2327 | /* Release the irq reservation */ | 2383 | if (phba->intr_type == MSIX) |
| 2328 | free_irq(phba->pcidev->irq, phba); | 2384 | lpfc_disable_msix(phba); |
| 2329 | if (phba->using_msi) | 2385 | else { |
| 2330 | pci_disable_msi(phba->pcidev); | 2386 | free_irq(phba->pcidev->irq, phba); |
| 2387 | if (phba->intr_type == MSI) | ||
| 2388 | pci_disable_msi(phba->pcidev); | ||
| 2389 | } | ||
| 2331 | 2390 | ||
| 2332 | /* Request a slot reset. */ | 2391 | /* Request a slot reset. */ |
| 2333 | return PCI_ERS_RESULT_NEED_RESET; | 2392 | return PCI_ERS_RESULT_NEED_RESET; |
