diff options
author | Seokmann Ju <seokmann.ju@qlogic.com> | 2007-09-20 17:07:36 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.localdomain> | 2007-10-12 14:49:47 -0400 |
commit | 14e660e677ddd3574247495aae4ef63eb8899072 (patch) | |
tree | fbf817d4e37887ae31ed46652f72440901c1f94b /drivers/scsi/qla2xxx | |
parent | b7cc176c9eb3aa6989ac099efd8bdd6d0eaa784a (diff) |
[SCSI] qla2xxx: Add PCI error recovery support.
Additional cleanups and
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/qla2xxx')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_def.h | 1 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_isr.c | 28 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 114 |
3 files changed, 142 insertions, 1 deletions
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index e1e342889b2..75ab898b02b 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
24 | #include <linux/workqueue.h> | 24 | #include <linux/workqueue.h> |
25 | #include <linux/firmware.h> | 25 | #include <linux/firmware.h> |
26 | #include <linux/aer.h> | ||
26 | #include <asm/semaphore.h> | 27 | #include <asm/semaphore.h> |
27 | 28 | ||
28 | #include <scsi/scsi.h> | 29 | #include <scsi/scsi.h> |
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index eecae9905ec..dcfb24b198f 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c | |||
@@ -34,6 +34,7 @@ qla2100_intr_handler(int irq, void *dev_id) | |||
34 | int status; | 34 | int status; |
35 | unsigned long flags; | 35 | unsigned long flags; |
36 | unsigned long iter; | 36 | unsigned long iter; |
37 | uint16_t hccr; | ||
37 | uint16_t mb[4]; | 38 | uint16_t mb[4]; |
38 | 39 | ||
39 | ha = (scsi_qla_host_t *) dev_id; | 40 | ha = (scsi_qla_host_t *) dev_id; |
@@ -48,7 +49,23 @@ qla2100_intr_handler(int irq, void *dev_id) | |||
48 | 49 | ||
49 | spin_lock_irqsave(&ha->hardware_lock, flags); | 50 | spin_lock_irqsave(&ha->hardware_lock, flags); |
50 | for (iter = 50; iter--; ) { | 51 | for (iter = 50; iter--; ) { |
51 | if ((RD_REG_WORD(®->istatus) & ISR_RISC_INT) == 0) | 52 | hccr = RD_REG_WORD(®->hccr); |
53 | if (hccr & HCCR_RISC_PAUSE) { | ||
54 | if (pci_channel_offline(ha->pdev)) | ||
55 | break; | ||
56 | |||
57 | /* | ||
58 | * Issue a "HARD" reset in order for the RISC interrupt | ||
59 | * bit to be cleared. Schedule a big hammmer to get | ||
60 | * out of the RISC PAUSED state. | ||
61 | */ | ||
62 | WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); | ||
63 | RD_REG_WORD(®->hccr); | ||
64 | |||
65 | ha->isp_ops->fw_dump(ha, 1); | ||
66 | set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); | ||
67 | break; | ||
68 | } else if ((RD_REG_WORD(®->istatus) & ISR_RISC_INT) == 0) | ||
52 | break; | 69 | break; |
53 | 70 | ||
54 | if (RD_REG_WORD(®->semaphore) & BIT_0) { | 71 | if (RD_REG_WORD(®->semaphore) & BIT_0) { |
@@ -127,6 +144,9 @@ qla2300_intr_handler(int irq, void *dev_id) | |||
127 | for (iter = 50; iter--; ) { | 144 | for (iter = 50; iter--; ) { |
128 | stat = RD_REG_DWORD(®->u.isp2300.host_status); | 145 | stat = RD_REG_DWORD(®->u.isp2300.host_status); |
129 | if (stat & HSR_RISC_PAUSED) { | 146 | if (stat & HSR_RISC_PAUSED) { |
147 | if (pci_channel_offline(ha->pdev)) | ||
148 | break; | ||
149 | |||
130 | hccr = RD_REG_WORD(®->hccr); | 150 | hccr = RD_REG_WORD(®->hccr); |
131 | if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8)) | 151 | if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8)) |
132 | qla_printk(KERN_INFO, ha, "Parity error -- " | 152 | qla_printk(KERN_INFO, ha, "Parity error -- " |
@@ -1499,6 +1519,9 @@ qla24xx_intr_handler(int irq, void *dev_id) | |||
1499 | for (iter = 50; iter--; ) { | 1519 | for (iter = 50; iter--; ) { |
1500 | stat = RD_REG_DWORD(®->host_status); | 1520 | stat = RD_REG_DWORD(®->host_status); |
1501 | if (stat & HSRX_RISC_PAUSED) { | 1521 | if (stat & HSRX_RISC_PAUSED) { |
1522 | if (pci_channel_offline(ha->pdev)) | ||
1523 | break; | ||
1524 | |||
1502 | hccr = RD_REG_DWORD(®->hccr); | 1525 | hccr = RD_REG_DWORD(®->hccr); |
1503 | 1526 | ||
1504 | qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " | 1527 | qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " |
@@ -1633,6 +1656,9 @@ qla24xx_msix_default(int irq, void *dev_id) | |||
1633 | for (iter = 50; iter--; ) { | 1656 | for (iter = 50; iter--; ) { |
1634 | stat = RD_REG_DWORD(®->host_status); | 1657 | stat = RD_REG_DWORD(®->host_status); |
1635 | if (stat & HSRX_RISC_PAUSED) { | 1658 | if (stat & HSRX_RISC_PAUSED) { |
1659 | if (pci_channel_offline(ha->pdev)) | ||
1660 | break; | ||
1661 | |||
1636 | hccr = RD_REG_DWORD(®->hccr); | 1662 | hccr = RD_REG_DWORD(®->hccr); |
1637 | 1663 | ||
1638 | qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " | 1664 | qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " |
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 2a03400b6f7..a8ab2d3447b 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c | |||
@@ -385,6 +385,11 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) | |||
385 | srb_t *sp; | 385 | srb_t *sp; |
386 | int rval; | 386 | int rval; |
387 | 387 | ||
388 | if (unlikely(pci_channel_offline(ha->pdev))) { | ||
389 | cmd->result = DID_REQUEUE << 16; | ||
390 | goto qc_fail_command; | ||
391 | } | ||
392 | |||
388 | rval = fc_remote_port_chkready(rport); | 393 | rval = fc_remote_port_chkready(rport); |
389 | if (rval) { | 394 | if (rval) { |
390 | cmd->result = rval; | 395 | cmd->result = rval; |
@@ -447,6 +452,11 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) | |||
447 | int rval; | 452 | int rval; |
448 | scsi_qla_host_t *pha = to_qla_parent(ha); | 453 | scsi_qla_host_t *pha = to_qla_parent(ha); |
449 | 454 | ||
455 | if (unlikely(pci_channel_offline(ha->pdev))) { | ||
456 | cmd->result = DID_REQUEUE << 16; | ||
457 | goto qc24_fail_command; | ||
458 | } | ||
459 | |||
450 | rval = fc_remote_port_chkready(rport); | 460 | rval = fc_remote_port_chkready(rport); |
451 | if (rval) { | 461 | if (rval) { |
452 | cmd->result = rval; | 462 | cmd->result = rval; |
@@ -1571,6 +1581,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1571 | if (pci_enable_device(pdev)) | 1581 | if (pci_enable_device(pdev)) |
1572 | goto probe_out; | 1582 | goto probe_out; |
1573 | 1583 | ||
1584 | if (pci_find_aer_capability(pdev)) | ||
1585 | if (pci_enable_pcie_error_reporting(pdev)) | ||
1586 | goto probe_out; | ||
1587 | |||
1574 | sht = &qla2x00_driver_template; | 1588 | sht = &qla2x00_driver_template; |
1575 | if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 || | 1589 | if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 || |
1576 | pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 || | 1590 | pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 || |
@@ -2814,6 +2828,105 @@ qla2x00_release_firmware(void) | |||
2814 | up(&qla_fw_lock); | 2828 | up(&qla_fw_lock); |
2815 | } | 2829 | } |
2816 | 2830 | ||
2831 | static pci_ers_result_t | ||
2832 | qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) | ||
2833 | { | ||
2834 | switch (state) { | ||
2835 | case pci_channel_io_normal: | ||
2836 | return PCI_ERS_RESULT_CAN_RECOVER; | ||
2837 | case pci_channel_io_frozen: | ||
2838 | pci_disable_device(pdev); | ||
2839 | return PCI_ERS_RESULT_NEED_RESET; | ||
2840 | case pci_channel_io_perm_failure: | ||
2841 | qla2x00_remove_one(pdev); | ||
2842 | return PCI_ERS_RESULT_DISCONNECT; | ||
2843 | } | ||
2844 | return PCI_ERS_RESULT_NEED_RESET; | ||
2845 | } | ||
2846 | |||
2847 | static pci_ers_result_t | ||
2848 | qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) | ||
2849 | { | ||
2850 | int risc_paused = 0; | ||
2851 | uint32_t stat; | ||
2852 | unsigned long flags; | ||
2853 | scsi_qla_host_t *ha = pci_get_drvdata(pdev); | ||
2854 | struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; | ||
2855 | struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; | ||
2856 | |||
2857 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
2858 | if (IS_QLA2100(ha) || IS_QLA2200(ha)){ | ||
2859 | stat = RD_REG_DWORD(®->hccr); | ||
2860 | if (stat & HCCR_RISC_PAUSE) | ||
2861 | risc_paused = 1; | ||
2862 | } else if (IS_QLA23XX(ha)) { | ||
2863 | stat = RD_REG_DWORD(®->u.isp2300.host_status); | ||
2864 | if (stat & HSR_RISC_PAUSED) | ||
2865 | risc_paused = 1; | ||
2866 | } else if (IS_FWI2_CAPABLE(ha)) { | ||
2867 | stat = RD_REG_DWORD(®24->host_status); | ||
2868 | if (stat & HSRX_RISC_PAUSED) | ||
2869 | risc_paused = 1; | ||
2870 | } | ||
2871 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
2872 | |||
2873 | if (risc_paused) { | ||
2874 | qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, " | ||
2875 | "Dumping firmware!\n"); | ||
2876 | ha->isp_ops->fw_dump(ha, 0); | ||
2877 | |||
2878 | return PCI_ERS_RESULT_NEED_RESET; | ||
2879 | } else | ||
2880 | return PCI_ERS_RESULT_RECOVERED; | ||
2881 | } | ||
2882 | |||
2883 | static pci_ers_result_t | ||
2884 | qla2xxx_pci_slot_reset(struct pci_dev *pdev) | ||
2885 | { | ||
2886 | pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT; | ||
2887 | scsi_qla_host_t *ha = pci_get_drvdata(pdev); | ||
2888 | |||
2889 | if (pci_enable_device(pdev)) { | ||
2890 | qla_printk(KERN_WARNING, ha, | ||
2891 | "Can't re-enable PCI device after reset.\n"); | ||
2892 | |||
2893 | return ret; | ||
2894 | } | ||
2895 | pci_set_master(pdev); | ||
2896 | |||
2897 | if (ha->isp_ops->pci_config(ha)) | ||
2898 | return ret; | ||
2899 | |||
2900 | set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); | ||
2901 | if (qla2x00_abort_isp(ha)== QLA_SUCCESS) | ||
2902 | ret = PCI_ERS_RESULT_RECOVERED; | ||
2903 | clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); | ||
2904 | |||
2905 | return ret; | ||
2906 | } | ||
2907 | |||
2908 | static void | ||
2909 | qla2xxx_pci_resume(struct pci_dev *pdev) | ||
2910 | { | ||
2911 | scsi_qla_host_t *ha = pci_get_drvdata(pdev); | ||
2912 | int ret; | ||
2913 | |||
2914 | ret = qla2x00_wait_for_hba_online(ha); | ||
2915 | if (ret != QLA_SUCCESS) { | ||
2916 | qla_printk(KERN_ERR, ha, | ||
2917 | "the device failed to resume I/O " | ||
2918 | "from slot/link_reset"); | ||
2919 | } | ||
2920 | pci_cleanup_aer_uncorrect_error_status(pdev); | ||
2921 | } | ||
2922 | |||
2923 | static struct pci_error_handlers qla2xxx_err_handler = { | ||
2924 | .error_detected = qla2xxx_pci_error_detected, | ||
2925 | .mmio_enabled = qla2xxx_pci_mmio_enabled, | ||
2926 | .slot_reset = qla2xxx_pci_slot_reset, | ||
2927 | .resume = qla2xxx_pci_resume, | ||
2928 | }; | ||
2929 | |||
2817 | static struct pci_device_id qla2xxx_pci_tbl[] = { | 2930 | static struct pci_device_id qla2xxx_pci_tbl[] = { |
2818 | { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) }, | 2931 | { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) }, |
2819 | { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) }, | 2932 | { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) }, |
@@ -2839,6 +2952,7 @@ static struct pci_driver qla2xxx_pci_driver = { | |||
2839 | .id_table = qla2xxx_pci_tbl, | 2952 | .id_table = qla2xxx_pci_tbl, |
2840 | .probe = qla2x00_probe_one, | 2953 | .probe = qla2x00_probe_one, |
2841 | .remove = __devexit_p(qla2x00_remove_one), | 2954 | .remove = __devexit_p(qla2x00_remove_one), |
2955 | .err_handler = &qla2xxx_err_handler, | ||
2842 | }; | 2956 | }; |
2843 | 2957 | ||
2844 | /** | 2958 | /** |