diff options
author | Lalit Chandivade <lalit.chandivade@qlogic.com> | 2010-07-30 05:08:47 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-08-06 13:00:40 -0400 |
commit | 2232be0d5707cd331b92027c0fd7ea5e843c2121 (patch) | |
tree | b391f987edc2ede2924bf2963beab4bc9f2eff26 /drivers/scsi | |
parent | 21033639699d883668f6937b03e7b710771ad37e (diff) |
[SCSI] qla4xxx: Added AER support for ISP82xx
Added support for PCI error handling
Signed-off-by: Lalit Chandivade <lalit.chandivade@qlogic.com>
Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: Poornima Vonti <poornima.vonti@qlogic.com>
Signed-off-by: Ravi Anand <ravi.anand@qlogic.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_def.h | 30 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_glbl.h | 1 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_init.c | 5 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_isr.c | 3 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_mbx.c | 8 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_nx.c | 8 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_os.c | 301 |
7 files changed, 352 insertions, 4 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index 6c9262753243..9dc0a6616edd 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h | |||
@@ -36,6 +36,24 @@ | |||
36 | #include "ql4_dbg.h" | 36 | #include "ql4_dbg.h" |
37 | #include "ql4_nx.h" | 37 | #include "ql4_nx.h" |
38 | 38 | ||
39 | #if defined(CONFIG_PCIEAER) | ||
40 | #include <linux/aer.h> | ||
41 | #else | ||
42 | /* AER releated */ | ||
43 | static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev) | ||
44 | { | ||
45 | return -EINVAL; | ||
46 | } | ||
47 | static inline int pci_disable_pcie_error_reporting(struct pci_dev *dev) | ||
48 | { | ||
49 | return -EINVAL; | ||
50 | } | ||
51 | static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) | ||
52 | { | ||
53 | return -EINVAL; | ||
54 | } | ||
55 | #endif | ||
56 | |||
39 | #ifndef PCI_DEVICE_ID_QLOGIC_ISP4010 | 57 | #ifndef PCI_DEVICE_ID_QLOGIC_ISP4010 |
40 | #define PCI_DEVICE_ID_QLOGIC_ISP4010 0x4010 | 58 | #define PCI_DEVICE_ID_QLOGIC_ISP4010 0x4010 |
41 | #endif | 59 | #endif |
@@ -381,7 +399,8 @@ struct scsi_qla_host { | |||
381 | #define AF_MSIX_ENABLED 17 /* 0x00020000 */ | 399 | #define AF_MSIX_ENABLED 17 /* 0x00020000 */ |
382 | #define AF_MBOX_COMMAND_NOPOLL 18 /* 0x00040000 */ | 400 | #define AF_MBOX_COMMAND_NOPOLL 18 /* 0x00040000 */ |
383 | #define AF_FW_RECOVERY 19 /* 0x00080000 */ | 401 | #define AF_FW_RECOVERY 19 /* 0x00080000 */ |
384 | 402 | #define AF_EEH_BUSY 20 /* 0x00100000 */ | |
403 | #define AF_PCI_CHANNEL_IO_PERM_FAILURE 21 /* 0x00200000 */ | ||
385 | 404 | ||
386 | unsigned long dpc_flags; | 405 | unsigned long dpc_flags; |
387 | 406 | ||
@@ -617,6 +636,15 @@ static inline int is_qla8022(struct scsi_qla_host *ha) | |||
617 | return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022; | 636 | return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022; |
618 | } | 637 | } |
619 | 638 | ||
639 | /* Note: Currently AER/EEH is now supported only for 8022 cards | ||
640 | * This function needs to be updated when AER/EEH is enabled | ||
641 | * for other cards. | ||
642 | */ | ||
643 | static inline int is_aer_supported(struct scsi_qla_host *ha) | ||
644 | { | ||
645 | return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022; | ||
646 | } | ||
647 | |||
620 | static inline int adapter_up(struct scsi_qla_host *ha) | 648 | static inline int adapter_up(struct scsi_qla_host *ha) |
621 | { | 649 | { |
622 | return (test_bit(AF_ONLINE, &ha->flags) != 0) && | 650 | return (test_bit(AF_ONLINE, &ha->flags) != 0) && |
diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index ea3db23602e6..f065204e401b 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h | |||
@@ -132,6 +132,7 @@ void qla4_8xxx_idc_unlock(struct scsi_qla_host *ha); | |||
132 | int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha); | 132 | int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha); |
133 | void qla4_8xxx_need_qsnt_handler(struct scsi_qla_host *ha); | 133 | void qla4_8xxx_need_qsnt_handler(struct scsi_qla_host *ha); |
134 | void qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha); | 134 | void qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha); |
135 | inline void qla4_8xxx_set_drv_active(struct scsi_qla_host *ha); | ||
135 | 136 | ||
136 | extern int ql4xextended_error_logging; | 137 | extern int ql4xextended_error_logging; |
137 | extern int ql4xdiscoverywait; | 138 | extern int ql4xdiscoverywait; |
diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 39048d9339ac..4c9be77ee70b 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c | |||
@@ -308,7 +308,6 @@ static int qla4xxx_fw_ready(struct scsi_qla_host *ha) | |||
308 | DEBUG2(printk("scsi%ld: %s: unable to get firmware " | 308 | DEBUG2(printk("scsi%ld: %s: unable to get firmware " |
309 | "state\n", ha->host_no, __func__)); | 309 | "state\n", ha->host_no, __func__)); |
310 | break; | 310 | break; |
311 | |||
312 | } | 311 | } |
313 | 312 | ||
314 | if (ha->firmware_state & FW_STATE_ERROR) { | 313 | if (ha->firmware_state & FW_STATE_ERROR) { |
@@ -445,6 +444,10 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha) | |||
445 | { | 444 | { |
446 | int status = QLA_ERROR; | 445 | int status = QLA_ERROR; |
447 | 446 | ||
447 | if (is_aer_supported(ha) && | ||
448 | test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags)) | ||
449 | return status; | ||
450 | |||
448 | /* For 82xx, stop firmware before initializing because if BIOS | 451 | /* For 82xx, stop firmware before initializing because if BIOS |
449 | * has previously initialized firmware, then driver's initialize | 452 | * has previously initialized firmware, then driver's initialize |
450 | * firmware will fail. */ | 453 | * firmware will fail. */ |
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index aa65697a86b4..2a1ab63f3eb0 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c | |||
@@ -816,6 +816,9 @@ irqreturn_t qla4_8xxx_intr_handler(int irq, void *dev_id) | |||
816 | unsigned long flags = 0; | 816 | unsigned long flags = 0; |
817 | uint8_t reqs_count = 0; | 817 | uint8_t reqs_count = 0; |
818 | 818 | ||
819 | if (unlikely(pci_channel_offline(ha->pdev))) | ||
820 | return IRQ_HANDLED; | ||
821 | |||
819 | ha->isr_count++; | 822 | ha->isr_count++; |
820 | status = qla4_8xxx_rd_32(ha, ISR_INT_VECTOR); | 823 | status = qla4_8xxx_rd_32(ha, ISR_INT_VECTOR); |
821 | if (!(status & ha->nx_legacy_intr.int_vec_bit)) | 824 | if (!(status & ha->nx_legacy_intr.int_vec_bit)) |
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index 1003e48d2200..90021704d8ca 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c | |||
@@ -48,6 +48,13 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, | |||
48 | return status; | 48 | return status; |
49 | } | 49 | } |
50 | 50 | ||
51 | if ((is_aer_supported(ha)) && | ||
52 | (test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags))) { | ||
53 | DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Perm failure on EEH, " | ||
54 | "timeout MBX Exiting.\n", ha->host_no, __func__)); | ||
55 | return status; | ||
56 | } | ||
57 | |||
51 | /* Mailbox code active */ | 58 | /* Mailbox code active */ |
52 | wait_count = MBOX_TOV * 100; | 59 | wait_count = MBOX_TOV * 100; |
53 | 60 | ||
@@ -159,6 +166,7 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, | |||
159 | while (test_bit(AF_MBOX_COMMAND_DONE, &ha->flags) == 0) { | 166 | while (test_bit(AF_MBOX_COMMAND_DONE, &ha->flags) == 0) { |
160 | if (time_after_eq(jiffies, wait_count)) | 167 | if (time_after_eq(jiffies, wait_count)) |
161 | break; | 168 | break; |
169 | |||
162 | /* | 170 | /* |
163 | * Service the interrupt. | 171 | * Service the interrupt. |
164 | * The ISR will save the mailbox status registers | 172 | * The ISR will save the mailbox status registers |
diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 0830ea9d708c..1b85235efd8c 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c | |||
@@ -1418,7 +1418,7 @@ static int qla4_8xxx_rcvpeg_ready(struct scsi_qla_host *ha) | |||
1418 | return QLA_SUCCESS; | 1418 | return QLA_SUCCESS; |
1419 | } | 1419 | } |
1420 | 1420 | ||
1421 | static inline void | 1421 | inline void |
1422 | qla4_8xxx_set_drv_active(struct scsi_qla_host *ha) | 1422 | qla4_8xxx_set_drv_active(struct scsi_qla_host *ha) |
1423 | { | 1423 | { |
1424 | uint32_t drv_active; | 1424 | uint32_t drv_active; |
@@ -1441,11 +1441,15 @@ qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha) | |||
1441 | static inline int | 1441 | static inline int |
1442 | qla4_8xxx_need_reset(struct scsi_qla_host *ha) | 1442 | qla4_8xxx_need_reset(struct scsi_qla_host *ha) |
1443 | { | 1443 | { |
1444 | uint32_t drv_state; | 1444 | uint32_t drv_state, drv_active; |
1445 | int rval; | 1445 | int rval; |
1446 | 1446 | ||
1447 | drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); | ||
1447 | drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE); | 1448 | drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE); |
1448 | rval = drv_state & (1 << (ha->func_num * 4)); | 1449 | rval = drv_state & (1 << (ha->func_num * 4)); |
1450 | if ((test_bit(AF_EEH_BUSY, &ha->flags)) && drv_active) | ||
1451 | rval = 1; | ||
1452 | |||
1449 | return rval; | 1453 | return rval; |
1450 | } | 1454 | } |
1451 | 1455 | ||
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 2bb362c6e634..370d40ff1529 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c | |||
@@ -475,6 +475,14 @@ static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, | |||
475 | struct srb *srb; | 475 | struct srb *srb; |
476 | int rval; | 476 | int rval; |
477 | 477 | ||
478 | if (test_bit(AF_EEH_BUSY, &ha->flags)) { | ||
479 | if (test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags)) | ||
480 | cmd->result = DID_NO_CONNECT << 16; | ||
481 | else | ||
482 | cmd->result = DID_REQUEUE << 16; | ||
483 | goto qc_fail_command; | ||
484 | } | ||
485 | |||
478 | if (!sess) { | 486 | if (!sess) { |
479 | cmd->result = DID_IMM_RETRY << 16; | 487 | cmd->result = DID_IMM_RETRY << 16; |
480 | goto qc_fail_command; | 488 | goto qc_fail_command; |
@@ -655,6 +663,13 @@ static void qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha) | |||
655 | uint32_t fw_heartbeat_counter, halt_status; | 663 | uint32_t fw_heartbeat_counter, halt_status; |
656 | 664 | ||
657 | fw_heartbeat_counter = qla4_8xxx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); | 665 | fw_heartbeat_counter = qla4_8xxx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); |
666 | /* If PEG_ALIVE_COUNTER is 0xffffffff, AER/EEH is in progress, ignore */ | ||
667 | if (fw_heartbeat_counter == 0xffffffff) { | ||
668 | DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Device in frozen " | ||
669 | "state, QLA82XX_PEG_ALIVE_COUNTER is 0xffffffff\n", | ||
670 | ha->host_no, __func__)); | ||
671 | return; | ||
672 | } | ||
658 | 673 | ||
659 | if (ha->fw_heartbeat_counter == fw_heartbeat_counter) { | 674 | if (ha->fw_heartbeat_counter == fw_heartbeat_counter) { |
660 | ha->seconds_since_last_heartbeat++; | 675 | ha->seconds_since_last_heartbeat++; |
@@ -723,6 +738,19 @@ static void qla4xxx_timer(struct scsi_qla_host *ha) | |||
723 | { | 738 | { |
724 | struct ddb_entry *ddb_entry, *dtemp; | 739 | struct ddb_entry *ddb_entry, *dtemp; |
725 | int start_dpc = 0; | 740 | int start_dpc = 0; |
741 | uint16_t w; | ||
742 | |||
743 | /* If we are in the middle of AER/EEH processing | ||
744 | * skip any processing and reschedule the timer | ||
745 | */ | ||
746 | if (test_bit(AF_EEH_BUSY, &ha->flags)) { | ||
747 | mod_timer(&ha->timer, jiffies + HZ); | ||
748 | return; | ||
749 | } | ||
750 | |||
751 | /* Hardware read to trigger an EEH error during mailbox waits. */ | ||
752 | if (!pci_channel_offline(ha->pdev)) | ||
753 | pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); | ||
726 | 754 | ||
727 | if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) { | 755 | if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) { |
728 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s exited. HBA GOING AWAY\n", | 756 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s exited. HBA GOING AWAY\n", |
@@ -1213,6 +1241,12 @@ static void qla4xxx_do_dpc(struct work_struct *work) | |||
1213 | if (!test_bit(AF_INIT_DONE, &ha->flags)) | 1241 | if (!test_bit(AF_INIT_DONE, &ha->flags)) |
1214 | goto do_dpc_exit; | 1242 | goto do_dpc_exit; |
1215 | 1243 | ||
1244 | if (test_bit(AF_EEH_BUSY, &ha->flags)) { | ||
1245 | DEBUG2(printk(KERN_INFO "scsi%ld: %s: flags = %lx\n", | ||
1246 | ha->host_no, __func__, ha->flags)); | ||
1247 | goto do_dpc_exit; | ||
1248 | } | ||
1249 | |||
1216 | /* HBA is in the process of being permanently disabled. | 1250 | /* HBA is in the process of being permanently disabled. |
1217 | * Don't process anything */ | 1251 | * Don't process anything */ |
1218 | if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) | 1252 | if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) |
@@ -1618,6 +1652,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, | |||
1618 | ha->host = host; | 1652 | ha->host = host; |
1619 | ha->host_no = host->host_no; | 1653 | ha->host_no = host->host_no; |
1620 | 1654 | ||
1655 | pci_enable_pcie_error_reporting(pdev); | ||
1656 | |||
1621 | /* Setup Runtime configurable options */ | 1657 | /* Setup Runtime configurable options */ |
1622 | if (is_qla8022(ha)) { | 1658 | if (is_qla8022(ha)) { |
1623 | ha->isp_ops = &qla4_8xxx_isp_ops; | 1659 | ha->isp_ops = &qla4_8xxx_isp_ops; |
@@ -1636,6 +1672,10 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, | |||
1636 | ha->isp_ops = &qla4xxx_isp_ops; | 1672 | ha->isp_ops = &qla4xxx_isp_ops; |
1637 | } | 1673 | } |
1638 | 1674 | ||
1675 | /* Set EEH reset type to fundamental if required by hba */ | ||
1676 | if (is_qla8022(ha)) | ||
1677 | pdev->needs_freset = 1; | ||
1678 | |||
1639 | /* Configure PCI I/O space. */ | 1679 | /* Configure PCI I/O space. */ |
1640 | ret = ha->isp_ops->iospace_config(ha); | 1680 | ret = ha->isp_ops->iospace_config(ha); |
1641 | if (ret) | 1681 | if (ret) |
@@ -1732,6 +1772,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, | |||
1732 | } | 1772 | } |
1733 | } | 1773 | } |
1734 | 1774 | ||
1775 | pci_save_state(ha->pdev); | ||
1735 | ha->isp_ops->enable_intrs(ha); | 1776 | ha->isp_ops->enable_intrs(ha); |
1736 | 1777 | ||
1737 | /* Start timer thread. */ | 1778 | /* Start timer thread. */ |
@@ -1758,6 +1799,7 @@ probe_failed: | |||
1758 | qla4xxx_free_adapter(ha); | 1799 | qla4xxx_free_adapter(ha); |
1759 | 1800 | ||
1760 | probe_failed_ioconfig: | 1801 | probe_failed_ioconfig: |
1802 | pci_disable_pcie_error_reporting(pdev); | ||
1761 | scsi_host_put(ha->host); | 1803 | scsi_host_put(ha->host); |
1762 | 1804 | ||
1763 | probe_disable_device: | 1805 | probe_disable_device: |
@@ -1787,6 +1829,7 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev) | |||
1787 | 1829 | ||
1788 | scsi_host_put(ha->host); | 1830 | scsi_host_put(ha->host); |
1789 | 1831 | ||
1832 | pci_disable_pcie_error_reporting(pdev); | ||
1790 | pci_disable_device(pdev); | 1833 | pci_disable_device(pdev); |
1791 | pci_set_drvdata(pdev, NULL); | 1834 | pci_set_drvdata(pdev, NULL); |
1792 | } | 1835 | } |
@@ -1883,6 +1926,17 @@ static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha, | |||
1883 | int done = 0; | 1926 | int done = 0; |
1884 | struct srb *rp; | 1927 | struct srb *rp; |
1885 | uint32_t max_wait_time = EH_WAIT_CMD_TOV; | 1928 | uint32_t max_wait_time = EH_WAIT_CMD_TOV; |
1929 | int ret = SUCCESS; | ||
1930 | |||
1931 | /* Dont wait on command if PCI error is being handled | ||
1932 | * by PCI AER driver | ||
1933 | */ | ||
1934 | if (unlikely(pci_channel_offline(ha->pdev)) || | ||
1935 | (test_bit(AF_EEH_BUSY, &ha->flags))) { | ||
1936 | ql4_printk(KERN_WARNING, ha, "scsi%ld: Return from %s\n", | ||
1937 | ha->host_no, __func__); | ||
1938 | return ret; | ||
1939 | } | ||
1886 | 1940 | ||
1887 | do { | 1941 | do { |
1888 | /* Checking to see if its returned to OS */ | 1942 | /* Checking to see if its returned to OS */ |
@@ -2178,6 +2232,252 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd) | |||
2178 | return return_status; | 2232 | return return_status; |
2179 | } | 2233 | } |
2180 | 2234 | ||
2235 | /* PCI AER driver recovers from all correctable errors w/o | ||
2236 | * driver intervention. For uncorrectable errors PCI AER | ||
2237 | * driver calls the following device driver's callbacks | ||
2238 | * | ||
2239 | * - Fatal Errors - link_reset | ||
2240 | * - Non-Fatal Errors - driver's pci_error_detected() which | ||
2241 | * returns CAN_RECOVER, NEED_RESET or DISCONNECT. | ||
2242 | * | ||
2243 | * PCI AER driver calls | ||
2244 | * CAN_RECOVER - driver's pci_mmio_enabled(), mmio_enabled | ||
2245 | * returns RECOVERED or NEED_RESET if fw_hung | ||
2246 | * NEED_RESET - driver's slot_reset() | ||
2247 | * DISCONNECT - device is dead & cannot recover | ||
2248 | * RECOVERED - driver's pci_resume() | ||
2249 | */ | ||
2250 | static pci_ers_result_t | ||
2251 | qla4xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) | ||
2252 | { | ||
2253 | struct scsi_qla_host *ha = pci_get_drvdata(pdev); | ||
2254 | |||
2255 | ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: error detected:state %x\n", | ||
2256 | ha->host_no, __func__, state); | ||
2257 | |||
2258 | if (!is_aer_supported(ha)) | ||
2259 | return PCI_ERS_RESULT_NONE; | ||
2260 | |||
2261 | switch (state) { | ||
2262 | case pci_channel_io_normal: | ||
2263 | clear_bit(AF_EEH_BUSY, &ha->flags); | ||
2264 | return PCI_ERS_RESULT_CAN_RECOVER; | ||
2265 | case pci_channel_io_frozen: | ||
2266 | set_bit(AF_EEH_BUSY, &ha->flags); | ||
2267 | qla4xxx_mailbox_premature_completion(ha); | ||
2268 | qla4xxx_free_irqs(ha); | ||
2269 | pci_disable_device(pdev); | ||
2270 | return PCI_ERS_RESULT_NEED_RESET; | ||
2271 | case pci_channel_io_perm_failure: | ||
2272 | set_bit(AF_EEH_BUSY, &ha->flags); | ||
2273 | set_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags); | ||
2274 | qla4xxx_abort_active_cmds(ha, DID_NO_CONNECT << 16); | ||
2275 | return PCI_ERS_RESULT_DISCONNECT; | ||
2276 | } | ||
2277 | return PCI_ERS_RESULT_NEED_RESET; | ||
2278 | } | ||
2279 | |||
2280 | /** | ||
2281 | * qla4xxx_pci_mmio_enabled() gets called if | ||
2282 | * qla4xxx_pci_error_detected() returns PCI_ERS_RESULT_CAN_RECOVER | ||
2283 | * and read/write to the device still works. | ||
2284 | **/ | ||
2285 | static pci_ers_result_t | ||
2286 | qla4xxx_pci_mmio_enabled(struct pci_dev *pdev) | ||
2287 | { | ||
2288 | struct scsi_qla_host *ha = pci_get_drvdata(pdev); | ||
2289 | |||
2290 | if (!is_aer_supported(ha)) | ||
2291 | return PCI_ERS_RESULT_NONE; | ||
2292 | |||
2293 | if (test_bit(AF_FW_RECOVERY, &ha->flags)) { | ||
2294 | ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: firmware hang -- " | ||
2295 | "mmio_enabled\n", ha->host_no, __func__); | ||
2296 | return PCI_ERS_RESULT_NEED_RESET; | ||
2297 | } else | ||
2298 | return PCI_ERS_RESULT_RECOVERED; | ||
2299 | } | ||
2300 | |||
2301 | uint32_t qla4_8xxx_error_recovery(struct scsi_qla_host *ha) | ||
2302 | { | ||
2303 | uint32_t rval = QLA_ERROR; | ||
2304 | int fn; | ||
2305 | struct pci_dev *other_pdev = NULL; | ||
2306 | |||
2307 | ql4_printk(KERN_WARNING, ha, "scsi%ld: In %s\n", ha->host_no, __func__); | ||
2308 | |||
2309 | set_bit(DPC_RESET_ACTIVE, &ha->dpc_flags); | ||
2310 | |||
2311 | if (test_bit(AF_ONLINE, &ha->flags)) { | ||
2312 | clear_bit(AF_ONLINE, &ha->flags); | ||
2313 | qla4xxx_mark_all_devices_missing(ha); | ||
2314 | qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); | ||
2315 | qla4xxx_abort_active_cmds(ha, DID_RESET << 16); | ||
2316 | } | ||
2317 | |||
2318 | fn = PCI_FUNC(ha->pdev->devfn); | ||
2319 | while (fn > 0) { | ||
2320 | fn--; | ||
2321 | ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Finding PCI device at " | ||
2322 | "func %x\n", ha->host_no, __func__, fn); | ||
2323 | /* Get the pci device given the domain, bus, | ||
2324 | * slot/function number */ | ||
2325 | other_pdev = | ||
2326 | pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus), | ||
2327 | ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn), | ||
2328 | fn)); | ||
2329 | |||
2330 | if (!other_pdev) | ||
2331 | continue; | ||
2332 | |||
2333 | if (atomic_read(&other_pdev->enable_cnt)) { | ||
2334 | ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Found PCI " | ||
2335 | "func in enabled state%x\n", ha->host_no, | ||
2336 | __func__, fn); | ||
2337 | pci_dev_put(other_pdev); | ||
2338 | break; | ||
2339 | } | ||
2340 | pci_dev_put(other_pdev); | ||
2341 | } | ||
2342 | |||
2343 | /* The first function on the card, the reset owner will | ||
2344 | * start & initialize the firmware. The other functions | ||
2345 | * on the card will reset the firmware context | ||
2346 | */ | ||
2347 | if (!fn) { | ||
2348 | ql4_printk(KERN_INFO, ha, "scsi%ld: %s: devfn being reset " | ||
2349 | "0x%x is the owner\n", ha->host_no, __func__, | ||
2350 | ha->pdev->devfn); | ||
2351 | |||
2352 | qla4_8xxx_idc_lock(ha); | ||
2353 | qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | ||
2354 | QLA82XX_DEV_COLD); | ||
2355 | |||
2356 | qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, | ||
2357 | QLA82XX_IDC_VERSION); | ||
2358 | |||
2359 | qla4_8xxx_idc_unlock(ha); | ||
2360 | clear_bit(AF_FW_RECOVERY, &ha->flags); | ||
2361 | rval = qla4xxx_initialize_adapter(ha, PRESERVE_DDB_LIST); | ||
2362 | qla4_8xxx_idc_lock(ha); | ||
2363 | |||
2364 | if (rval != QLA_SUCCESS) { | ||
2365 | ql4_printk(KERN_INFO, ha, "scsi%ld: %s: HW State: " | ||
2366 | "FAILED\n", ha->host_no, __func__); | ||
2367 | qla4_8xxx_clear_drv_active(ha); | ||
2368 | qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | ||
2369 | QLA82XX_DEV_FAILED); | ||
2370 | } else { | ||
2371 | ql4_printk(KERN_INFO, ha, "scsi%ld: %s: HW State: " | ||
2372 | "READY\n", ha->host_no, __func__); | ||
2373 | qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | ||
2374 | QLA82XX_DEV_READY); | ||
2375 | /* Clear driver state register */ | ||
2376 | qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0); | ||
2377 | qla4_8xxx_set_drv_active(ha); | ||
2378 | ha->isp_ops->enable_intrs(ha); | ||
2379 | } | ||
2380 | qla4_8xxx_idc_unlock(ha); | ||
2381 | } else { | ||
2382 | ql4_printk(KERN_INFO, ha, "scsi%ld: %s: devfn 0x%x is not " | ||
2383 | "the reset owner\n", ha->host_no, __func__, | ||
2384 | ha->pdev->devfn); | ||
2385 | if ((qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE) == | ||
2386 | QLA82XX_DEV_READY)) { | ||
2387 | clear_bit(AF_FW_RECOVERY, &ha->flags); | ||
2388 | rval = qla4xxx_initialize_adapter(ha, | ||
2389 | PRESERVE_DDB_LIST); | ||
2390 | if (rval == QLA_SUCCESS) | ||
2391 | ha->isp_ops->enable_intrs(ha); | ||
2392 | qla4_8xxx_idc_lock(ha); | ||
2393 | qla4_8xxx_set_drv_active(ha); | ||
2394 | qla4_8xxx_idc_unlock(ha); | ||
2395 | } | ||
2396 | } | ||
2397 | clear_bit(DPC_RESET_ACTIVE, &ha->dpc_flags); | ||
2398 | return rval; | ||
2399 | } | ||
2400 | |||
2401 | static pci_ers_result_t | ||
2402 | qla4xxx_pci_slot_reset(struct pci_dev *pdev) | ||
2403 | { | ||
2404 | pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT; | ||
2405 | struct scsi_qla_host *ha = pci_get_drvdata(pdev); | ||
2406 | int rc; | ||
2407 | |||
2408 | ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: slot_reset\n", | ||
2409 | ha->host_no, __func__); | ||
2410 | |||
2411 | if (!is_aer_supported(ha)) | ||
2412 | return PCI_ERS_RESULT_NONE; | ||
2413 | |||
2414 | /* Restore the saved state of PCIe device - | ||
2415 | * BAR registers, PCI Config space, PCIX, MSI, | ||
2416 | * IOV states | ||
2417 | */ | ||
2418 | pci_restore_state(pdev); | ||
2419 | |||
2420 | /* pci_restore_state() clears the saved_state flag of the device | ||
2421 | * save restored state which resets saved_state flag | ||
2422 | */ | ||
2423 | pci_save_state(pdev); | ||
2424 | |||
2425 | /* Initialize device or resume if in suspended state */ | ||
2426 | rc = pci_enable_device(pdev); | ||
2427 | if (rc) { | ||
2428 | ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: Cant re-enable " | ||
2429 | "device after reset\n", ha->host_no, __func__); | ||
2430 | goto exit_slot_reset; | ||
2431 | } | ||
2432 | |||
2433 | ret = qla4xxx_request_irqs(ha); | ||
2434 | if (ret) { | ||
2435 | ql4_printk(KERN_WARNING, ha, "Failed to reserve interrupt %d" | ||
2436 | " already in use.\n", pdev->irq); | ||
2437 | goto exit_slot_reset; | ||
2438 | } | ||
2439 | |||
2440 | if (is_qla8022(ha)) { | ||
2441 | if (qla4_8xxx_error_recovery(ha) == QLA_SUCCESS) { | ||
2442 | ret = PCI_ERS_RESULT_RECOVERED; | ||
2443 | goto exit_slot_reset; | ||
2444 | } else | ||
2445 | goto exit_slot_reset; | ||
2446 | } | ||
2447 | |||
2448 | exit_slot_reset: | ||
2449 | ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: Return=%x\n" | ||
2450 | "device after reset\n", ha->host_no, __func__, ret); | ||
2451 | return ret; | ||
2452 | } | ||
2453 | |||
2454 | static void | ||
2455 | qla4xxx_pci_resume(struct pci_dev *pdev) | ||
2456 | { | ||
2457 | struct scsi_qla_host *ha = pci_get_drvdata(pdev); | ||
2458 | int ret; | ||
2459 | |||
2460 | ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: pci_resume\n", | ||
2461 | ha->host_no, __func__); | ||
2462 | |||
2463 | ret = qla4xxx_wait_for_hba_online(ha); | ||
2464 | if (ret != QLA_SUCCESS) { | ||
2465 | ql4_printk(KERN_ERR, ha, "scsi%ld: %s: the device failed to " | ||
2466 | "resume I/O from slot/link_reset\n", ha->host_no, | ||
2467 | __func__); | ||
2468 | } | ||
2469 | |||
2470 | pci_cleanup_aer_uncorrect_error_status(pdev); | ||
2471 | clear_bit(AF_EEH_BUSY, &ha->flags); | ||
2472 | } | ||
2473 | |||
2474 | static struct pci_error_handlers qla4xxx_err_handler = { | ||
2475 | .error_detected = qla4xxx_pci_error_detected, | ||
2476 | .mmio_enabled = qla4xxx_pci_mmio_enabled, | ||
2477 | .slot_reset = qla4xxx_pci_slot_reset, | ||
2478 | .resume = qla4xxx_pci_resume, | ||
2479 | }; | ||
2480 | |||
2181 | static struct pci_device_id qla4xxx_pci_tbl[] = { | 2481 | static struct pci_device_id qla4xxx_pci_tbl[] = { |
2182 | { | 2482 | { |
2183 | .vendor = PCI_VENDOR_ID_QLOGIC, | 2483 | .vendor = PCI_VENDOR_ID_QLOGIC, |
@@ -2212,6 +2512,7 @@ static struct pci_driver qla4xxx_pci_driver = { | |||
2212 | .id_table = qla4xxx_pci_tbl, | 2512 | .id_table = qla4xxx_pci_tbl, |
2213 | .probe = qla4xxx_probe_adapter, | 2513 | .probe = qla4xxx_probe_adapter, |
2214 | .remove = qla4xxx_remove_adapter, | 2514 | .remove = qla4xxx_remove_adapter, |
2515 | .err_handler = &qla4xxx_err_handler, | ||
2215 | }; | 2516 | }; |
2216 | 2517 | ||
2217 | static int __init qla4xxx_module_init(void) | 2518 | static int __init qla4xxx_module_init(void) |