diff options
author | James Smart <James.Smart@Emulex.Com> | 2008-02-08 18:49:51 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-02-11 18:52:57 -0500 |
commit | db2378e09151c855e8f92c1b4b2fb4fc5cd8cb40 (patch) | |
tree | 82a79f9f5529ddc39ea2a6968ec6951ce02ad746 /drivers | |
parent | 1b32f6aa9935ab88eac0d608a4b06369f5d9064a (diff) |
[SCSI] lpfc 8.2.5 : Add MSI-X single message support
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers')
-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; |