diff options
| -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) |
