diff options
author | Linas Vepstas <linas@austin.ibm.com> | 2007-02-14 15:28:36 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2007-02-16 10:42:38 -0500 |
commit | 8d63f375051bfb1506fa546db840af8510d1cd60 (patch) | |
tree | 5c1bca94dd02ed007e8914d0302e42a163e5fc86 /drivers/scsi | |
parent | 4520b0089b41151ab3cc3ae1ee92d0aa19d88aae (diff) |
[SCSI] lpfc: add PCI error recovery support
This patch adds PCI Error recovery support to the
Emulex Lightpulse Fibrechannel (lpfc) SCSI device driver.
Lightly tested at this point, works.
Signed-off-by: Linas Vepstas <linas@austin.ibm.com>
Acked-by: Bino.Sebastian@Emulex.Com
Acked-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 97 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 12 |
2 files changed, 109 insertions, 0 deletions
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index afca45cdbcef..9d014e5a81c4 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c | |||
@@ -518,6 +518,10 @@ lpfc_handle_eratt(struct lpfc_hba * phba) | |||
518 | struct lpfc_sli *psli = &phba->sli; | 518 | struct lpfc_sli *psli = &phba->sli; |
519 | struct lpfc_sli_ring *pring; | 519 | struct lpfc_sli_ring *pring; |
520 | uint32_t event_data; | 520 | uint32_t event_data; |
521 | /* If the pci channel is offline, ignore possible errors, | ||
522 | * since we cannot communicate with the pci card anyway. */ | ||
523 | if (pci_channel_offline(phba->pcidev)) | ||
524 | return; | ||
521 | 525 | ||
522 | if (phba->work_hs & HS_FFER6 || | 526 | if (phba->work_hs & HS_FFER6 || |
523 | phba->work_hs & HS_FFER5) { | 527 | phba->work_hs & HS_FFER5) { |
@@ -1797,6 +1801,92 @@ lpfc_pci_remove_one(struct pci_dev *pdev) | |||
1797 | pci_set_drvdata(pdev, NULL); | 1801 | pci_set_drvdata(pdev, NULL); |
1798 | } | 1802 | } |
1799 | 1803 | ||
1804 | /** | ||
1805 | * lpfc_io_error_detected - called when PCI error is detected | ||
1806 | * @pdev: Pointer to PCI device | ||
1807 | * @state: The current pci conneection state | ||
1808 | * | ||
1809 | * This function is called after a PCI bus error affecting | ||
1810 | * this device has been detected. | ||
1811 | */ | ||
1812 | static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev, | ||
1813 | pci_channel_state_t state) | ||
1814 | { | ||
1815 | struct Scsi_Host *host = pci_get_drvdata(pdev); | ||
1816 | struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata; | ||
1817 | struct lpfc_sli *psli = &phba->sli; | ||
1818 | struct lpfc_sli_ring *pring; | ||
1819 | |||
1820 | if (state == pci_channel_io_perm_failure) { | ||
1821 | lpfc_pci_remove_one(pdev); | ||
1822 | return PCI_ERS_RESULT_DISCONNECT; | ||
1823 | } | ||
1824 | pci_disable_device(pdev); | ||
1825 | /* | ||
1826 | * There may be I/Os dropped by the firmware. | ||
1827 | * Error iocb (I/O) on txcmplq and let the SCSI layer | ||
1828 | * retry it after re-establishing link. | ||
1829 | */ | ||
1830 | pring = &psli->ring[psli->fcp_ring]; | ||
1831 | lpfc_sli_abort_iocb_ring(phba, pring); | ||
1832 | |||
1833 | /* Request a slot reset. */ | ||
1834 | return PCI_ERS_RESULT_NEED_RESET; | ||
1835 | } | ||
1836 | |||
1837 | /** | ||
1838 | * lpfc_io_slot_reset - called after the pci bus has been reset. | ||
1839 | * @pdev: Pointer to PCI device | ||
1840 | * | ||
1841 | * Restart the card from scratch, as if from a cold-boot. | ||
1842 | */ | ||
1843 | static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev) | ||
1844 | { | ||
1845 | struct Scsi_Host *host = pci_get_drvdata(pdev); | ||
1846 | struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata; | ||
1847 | struct lpfc_sli *psli = &phba->sli; | ||
1848 | int bars = pci_select_bars(pdev, IORESOURCE_MEM); | ||
1849 | |||
1850 | dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n"); | ||
1851 | if (pci_enable_device_bars(pdev, bars)) { | ||
1852 | printk(KERN_ERR "lpfc: Cannot re-enable " | ||
1853 | "PCI device after reset.\n"); | ||
1854 | return PCI_ERS_RESULT_DISCONNECT; | ||
1855 | } | ||
1856 | |||
1857 | pci_set_master(pdev); | ||
1858 | |||
1859 | /* Re-establishing Link */ | ||
1860 | spin_lock_irq(phba->host->host_lock); | ||
1861 | phba->fc_flag |= FC_ESTABLISH_LINK; | ||
1862 | psli->sli_flag &= ~LPFC_SLI2_ACTIVE; | ||
1863 | spin_unlock_irq(phba->host->host_lock); | ||
1864 | |||
1865 | |||
1866 | /* Take device offline; this will perform cleanup */ | ||
1867 | lpfc_offline(phba); | ||
1868 | lpfc_sli_brdrestart(phba); | ||
1869 | |||
1870 | return PCI_ERS_RESULT_RECOVERED; | ||
1871 | } | ||
1872 | |||
1873 | /** | ||
1874 | * lpfc_io_resume - called when traffic can start flowing again. | ||
1875 | * @pdev: Pointer to PCI device | ||
1876 | * | ||
1877 | * This callback is called when the error recovery driver tells us that | ||
1878 | * its OK to resume normal operation. | ||
1879 | */ | ||
1880 | static void lpfc_io_resume(struct pci_dev *pdev) | ||
1881 | { | ||
1882 | struct Scsi_Host *host = pci_get_drvdata(pdev); | ||
1883 | struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata; | ||
1884 | |||
1885 | if (lpfc_online(phba) == 0) { | ||
1886 | mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60); | ||
1887 | } | ||
1888 | } | ||
1889 | |||
1800 | static struct pci_device_id lpfc_id_table[] = { | 1890 | static struct pci_device_id lpfc_id_table[] = { |
1801 | {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER, | 1891 | {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER, |
1802 | PCI_ANY_ID, PCI_ANY_ID, }, | 1892 | PCI_ANY_ID, PCI_ANY_ID, }, |
@@ -1857,11 +1947,18 @@ static struct pci_device_id lpfc_id_table[] = { | |||
1857 | 1947 | ||
1858 | MODULE_DEVICE_TABLE(pci, lpfc_id_table); | 1948 | MODULE_DEVICE_TABLE(pci, lpfc_id_table); |
1859 | 1949 | ||
1950 | static struct pci_error_handlers lpfc_err_handler = { | ||
1951 | .error_detected = lpfc_io_error_detected, | ||
1952 | .slot_reset = lpfc_io_slot_reset, | ||
1953 | .resume = lpfc_io_resume, | ||
1954 | }; | ||
1955 | |||
1860 | static struct pci_driver lpfc_driver = { | 1956 | static struct pci_driver lpfc_driver = { |
1861 | .name = LPFC_DRIVER_NAME, | 1957 | .name = LPFC_DRIVER_NAME, |
1862 | .id_table = lpfc_id_table, | 1958 | .id_table = lpfc_id_table, |
1863 | .probe = lpfc_pci_probe_one, | 1959 | .probe = lpfc_pci_probe_one, |
1864 | .remove = __devexit_p(lpfc_pci_remove_one), | 1960 | .remove = __devexit_p(lpfc_pci_remove_one), |
1961 | .err_handler = &lpfc_err_handler, | ||
1865 | }; | 1962 | }; |
1866 | 1963 | ||
1867 | static int __init | 1964 | static int __init |
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index a4128e19338a..9fb6960a8ada 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c | |||
@@ -2104,6 +2104,10 @@ lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) | |||
2104 | volatile uint32_t word0, ldata; | 2104 | volatile uint32_t word0, ldata; |
2105 | void __iomem *to_slim; | 2105 | void __iomem *to_slim; |
2106 | 2106 | ||
2107 | /* If the PCI channel is in offline state, do not post mbox. */ | ||
2108 | if (unlikely(pci_channel_offline(phba->pcidev))) | ||
2109 | return MBX_NOT_FINISHED; | ||
2110 | |||
2107 | psli = &phba->sli; | 2111 | psli = &phba->sli; |
2108 | 2112 | ||
2109 | spin_lock_irqsave(phba->host->host_lock, drvr_flag); | 2113 | spin_lock_irqsave(phba->host->host_lock, drvr_flag); |
@@ -2407,6 +2411,10 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, | |||
2407 | struct lpfc_iocbq *nextiocb; | 2411 | struct lpfc_iocbq *nextiocb; |
2408 | IOCB_t *iocb; | 2412 | IOCB_t *iocb; |
2409 | 2413 | ||
2414 | /* If the PCI channel is in offline state, do not post iocbs. */ | ||
2415 | if (unlikely(pci_channel_offline(phba->pcidev))) | ||
2416 | return IOCB_ERROR; | ||
2417 | |||
2410 | /* | 2418 | /* |
2411 | * We should never get an IOCB if we are in a < LINK_DOWN state | 2419 | * We should never get an IOCB if we are in a < LINK_DOWN state |
2412 | */ | 2420 | */ |
@@ -3154,6 +3162,10 @@ lpfc_intr_handler(int irq, void *dev_id) | |||
3154 | if (unlikely(!phba)) | 3162 | if (unlikely(!phba)) |
3155 | return IRQ_NONE; | 3163 | return IRQ_NONE; |
3156 | 3164 | ||
3165 | /* If the pci channel is offline, ignore all the interrupts. */ | ||
3166 | if (unlikely(pci_channel_offline(phba->pcidev))) | ||
3167 | return IRQ_NONE; | ||
3168 | |||
3157 | phba->sli.slistat.sli_intr++; | 3169 | phba->sli.slistat.sli_intr++; |
3158 | 3170 | ||
3159 | /* | 3171 | /* |