diff options
author | Chad Dupuis <chad.dupuis@qlogic.com> | 2013-10-30 03:38:16 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-12-19 10:38:58 -0500 |
commit | f3ddac1918fe963bcbf8d407a3a3c0881b47248b (patch) | |
tree | 307e736319180d8930154ce40309f017e3696673 | |
parent | fe1b806f4f7172b1eae18ddeebb7d8fb351043f7 (diff) |
[SCSI] qla2xxx: Disable adapter when we encounter a PCI disconnect.
If we become disconnected from the PCI bus/PCIe fabric, there can be long delays
in register reads which can cause erroneous decisions to be made and cause a
soft lockup if a lock is held too long. As a preventative measure, check for a
disconnection (register reads that return -1) and then disable the board if we
find ourselves in this condition. For now, check in our interrupt handlers and
the per adapter one second timer.
Signed-off-by: Chad Dupuis <chad.dupuis@qlogic.com>
Signed-off-by: Saurav Kashyap <saurav.kashyap@qlogic.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r-- | drivers/scsi/qla2xxx/qla_dbg.c | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_def.h | 1 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gbl.h | 4 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_isr.c | 50 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_mr.c | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_nx.c | 18 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 77 |
7 files changed, 148 insertions, 6 deletions
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index acc1ea422cbc..149a1b5f6616 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c | |||
@@ -11,7 +11,7 @@ | |||
11 | * ---------------------------------------------------------------------- | 11 | * ---------------------------------------------------------------------- |
12 | * | Level | Last Value Used | Holes | | 12 | * | Level | Last Value Used | Holes | |
13 | * ---------------------------------------------------------------------- | 13 | * ---------------------------------------------------------------------- |
14 | * | Module Init and Probe | 0x015a | 0x4b,0xba,0xfa | | 14 | * | Module Init and Probe | 0x015b | 0x4b,0xba,0xfa | |
15 | * | Mailbox commands | 0x1181 | 0x111a-0x111b | | 15 | * | Mailbox commands | 0x1181 | 0x111a-0x111b | |
16 | * | | | 0x1155-0x1158 | | 16 | * | | | 0x1155-0x1158 | |
17 | * | | | 0x1018-0x1019 | | 17 | * | | | 0x1018-0x1019 | |
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 82b18c0c7e08..0fb01e1e213c 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h | |||
@@ -3301,6 +3301,7 @@ struct qla_hw_data { | |||
3301 | struct work_struct nic_core_reset; | 3301 | struct work_struct nic_core_reset; |
3302 | struct work_struct idc_state_handler; | 3302 | struct work_struct idc_state_handler; |
3303 | struct work_struct nic_core_unrecoverable; | 3303 | struct work_struct nic_core_unrecoverable; |
3304 | struct work_struct board_disable; | ||
3304 | 3305 | ||
3305 | struct mr_data_fx00 mr; | 3306 | struct mr_data_fx00 mr; |
3306 | 3307 | ||
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index df52f73acabd..359d0d9e09d9 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h | |||
@@ -159,6 +159,9 @@ extern int qla83xx_clear_drv_presence(scsi_qla_host_t *vha); | |||
159 | extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha); | 159 | extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha); |
160 | extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); | 160 | extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); |
161 | 161 | ||
162 | extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); | ||
163 | extern void qla2x00_disable_board_on_pci_error(struct work_struct *); | ||
164 | |||
162 | /* | 165 | /* |
163 | * Global Functions in qla_mid.c source file. | 166 | * Global Functions in qla_mid.c source file. |
164 | */ | 167 | */ |
@@ -454,6 +457,7 @@ extern uint8_t *qla25xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, | |||
454 | extern int qla25xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, | 457 | extern int qla25xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, |
455 | uint32_t); | 458 | uint32_t); |
456 | extern int qla2x00_is_a_vp_did(scsi_qla_host_t *, uint32_t); | 459 | extern int qla2x00_is_a_vp_did(scsi_qla_host_t *, uint32_t); |
460 | bool qla2x00_check_reg_for_disconnect(scsi_qla_host_t *, uint32_t); | ||
457 | 461 | ||
458 | extern int qla2x00_beacon_on(struct scsi_qla_host *); | 462 | extern int qla2x00_beacon_on(struct scsi_qla_host *); |
459 | extern int qla2x00_beacon_off(struct scsi_qla_host *); | 463 | extern int qla2x00_beacon_off(struct scsi_qla_host *); |
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 2e6eae3298d7..0b1b297712ee 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c | |||
@@ -56,6 +56,16 @@ qla2100_intr_handler(int irq, void *dev_id) | |||
56 | vha = pci_get_drvdata(ha->pdev); | 56 | vha = pci_get_drvdata(ha->pdev); |
57 | for (iter = 50; iter--; ) { | 57 | for (iter = 50; iter--; ) { |
58 | hccr = RD_REG_WORD(®->hccr); | 58 | hccr = RD_REG_WORD(®->hccr); |
59 | /* Check for PCI disconnection */ | ||
60 | if (hccr == 0xffff) { | ||
61 | /* | ||
62 | * Schedule this on the default system workqueue so that | ||
63 | * all the adapter workqueues and the DPC thread can be | ||
64 | * shutdown cleanly. | ||
65 | */ | ||
66 | schedule_work(&ha->board_disable); | ||
67 | break; | ||
68 | } | ||
59 | if (hccr & HCCR_RISC_PAUSE) { | 69 | if (hccr & HCCR_RISC_PAUSE) { |
60 | if (pci_channel_offline(ha->pdev)) | 70 | if (pci_channel_offline(ha->pdev)) |
61 | break; | 71 | break; |
@@ -110,6 +120,22 @@ qla2100_intr_handler(int irq, void *dev_id) | |||
110 | return (IRQ_HANDLED); | 120 | return (IRQ_HANDLED); |
111 | } | 121 | } |
112 | 122 | ||
123 | bool | ||
124 | qla2x00_check_reg_for_disconnect(scsi_qla_host_t *vha, uint32_t reg) | ||
125 | { | ||
126 | /* Check for PCI disconnection */ | ||
127 | if (reg == 0xffffffff) { | ||
128 | /* | ||
129 | * Schedule this on the default system workqueue so that all the | ||
130 | * adapter workqueues and the DPC thread can be shutdown | ||
131 | * cleanly. | ||
132 | */ | ||
133 | schedule_work(&vha->hw->board_disable); | ||
134 | return true; | ||
135 | } else | ||
136 | return false; | ||
137 | } | ||
138 | |||
113 | /** | 139 | /** |
114 | * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. | 140 | * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. |
115 | * @irq: | 141 | * @irq: |
@@ -148,11 +174,14 @@ qla2300_intr_handler(int irq, void *dev_id) | |||
148 | vha = pci_get_drvdata(ha->pdev); | 174 | vha = pci_get_drvdata(ha->pdev); |
149 | for (iter = 50; iter--; ) { | 175 | for (iter = 50; iter--; ) { |
150 | stat = RD_REG_DWORD(®->u.isp2300.host_status); | 176 | stat = RD_REG_DWORD(®->u.isp2300.host_status); |
177 | if (qla2x00_check_reg_for_disconnect(vha, stat)) | ||
178 | break; | ||
151 | if (stat & HSR_RISC_PAUSED) { | 179 | if (stat & HSR_RISC_PAUSED) { |
152 | if (unlikely(pci_channel_offline(ha->pdev))) | 180 | if (unlikely(pci_channel_offline(ha->pdev))) |
153 | break; | 181 | break; |
154 | 182 | ||
155 | hccr = RD_REG_WORD(®->hccr); | 183 | hccr = RD_REG_WORD(®->hccr); |
184 | |||
156 | if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8)) | 185 | if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8)) |
157 | ql_log(ql_log_warn, vha, 0x5026, | 186 | ql_log(ql_log_warn, vha, 0x5026, |
158 | "Parity error -- HCCR=%x, Dumping " | 187 | "Parity error -- HCCR=%x, Dumping " |
@@ -2571,6 +2600,8 @@ qla24xx_intr_handler(int irq, void *dev_id) | |||
2571 | vha = pci_get_drvdata(ha->pdev); | 2600 | vha = pci_get_drvdata(ha->pdev); |
2572 | for (iter = 50; iter--; ) { | 2601 | for (iter = 50; iter--; ) { |
2573 | stat = RD_REG_DWORD(®->host_status); | 2602 | stat = RD_REG_DWORD(®->host_status); |
2603 | if (qla2x00_check_reg_for_disconnect(vha, stat)) | ||
2604 | break; | ||
2574 | if (stat & HSRX_RISC_PAUSED) { | 2605 | if (stat & HSRX_RISC_PAUSED) { |
2575 | if (unlikely(pci_channel_offline(ha->pdev))) | 2606 | if (unlikely(pci_channel_offline(ha->pdev))) |
2576 | break; | 2607 | break; |
@@ -2640,6 +2671,7 @@ qla24xx_msix_rsp_q(int irq, void *dev_id) | |||
2640 | struct device_reg_24xx __iomem *reg; | 2671 | struct device_reg_24xx __iomem *reg; |
2641 | struct scsi_qla_host *vha; | 2672 | struct scsi_qla_host *vha; |
2642 | unsigned long flags; | 2673 | unsigned long flags; |
2674 | uint32_t stat = 0; | ||
2643 | 2675 | ||
2644 | rsp = (struct rsp_que *) dev_id; | 2676 | rsp = (struct rsp_que *) dev_id; |
2645 | if (!rsp) { | 2677 | if (!rsp) { |
@@ -2653,11 +2685,19 @@ qla24xx_msix_rsp_q(int irq, void *dev_id) | |||
2653 | spin_lock_irqsave(&ha->hardware_lock, flags); | 2685 | spin_lock_irqsave(&ha->hardware_lock, flags); |
2654 | 2686 | ||
2655 | vha = pci_get_drvdata(ha->pdev); | 2687 | vha = pci_get_drvdata(ha->pdev); |
2688 | /* | ||
2689 | * Use host_status register to check to PCI disconnection before we | ||
2690 | * we process the response queue. | ||
2691 | */ | ||
2692 | stat = RD_REG_DWORD(®->host_status); | ||
2693 | if (qla2x00_check_reg_for_disconnect(vha, stat)) | ||
2694 | goto out; | ||
2656 | qla24xx_process_response_queue(vha, rsp); | 2695 | qla24xx_process_response_queue(vha, rsp); |
2657 | if (!ha->flags.disable_msix_handshake) { | 2696 | if (!ha->flags.disable_msix_handshake) { |
2658 | WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); | 2697 | WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); |
2659 | RD_REG_DWORD_RELAXED(®->hccr); | 2698 | RD_REG_DWORD_RELAXED(®->hccr); |
2660 | } | 2699 | } |
2700 | out: | ||
2661 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 2701 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
2662 | 2702 | ||
2663 | return IRQ_HANDLED; | 2703 | return IRQ_HANDLED; |
@@ -2667,9 +2707,11 @@ static irqreturn_t | |||
2667 | qla25xx_msix_rsp_q(int irq, void *dev_id) | 2707 | qla25xx_msix_rsp_q(int irq, void *dev_id) |
2668 | { | 2708 | { |
2669 | struct qla_hw_data *ha; | 2709 | struct qla_hw_data *ha; |
2710 | scsi_qla_host_t *vha; | ||
2670 | struct rsp_que *rsp; | 2711 | struct rsp_que *rsp; |
2671 | struct device_reg_24xx __iomem *reg; | 2712 | struct device_reg_24xx __iomem *reg; |
2672 | unsigned long flags; | 2713 | unsigned long flags; |
2714 | uint32_t hccr = 0; | ||
2673 | 2715 | ||
2674 | rsp = (struct rsp_que *) dev_id; | 2716 | rsp = (struct rsp_que *) dev_id; |
2675 | if (!rsp) { | 2717 | if (!rsp) { |
@@ -2678,17 +2720,21 @@ qla25xx_msix_rsp_q(int irq, void *dev_id) | |||
2678 | return IRQ_NONE; | 2720 | return IRQ_NONE; |
2679 | } | 2721 | } |
2680 | ha = rsp->hw; | 2722 | ha = rsp->hw; |
2723 | vha = pci_get_drvdata(ha->pdev); | ||
2681 | 2724 | ||
2682 | /* Clear the interrupt, if enabled, for this response queue */ | 2725 | /* Clear the interrupt, if enabled, for this response queue */ |
2683 | if (!ha->flags.disable_msix_handshake) { | 2726 | if (!ha->flags.disable_msix_handshake) { |
2684 | reg = &ha->iobase->isp24; | 2727 | reg = &ha->iobase->isp24; |
2685 | spin_lock_irqsave(&ha->hardware_lock, flags); | 2728 | spin_lock_irqsave(&ha->hardware_lock, flags); |
2686 | WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); | 2729 | WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); |
2687 | RD_REG_DWORD_RELAXED(®->hccr); | 2730 | hccr = RD_REG_DWORD_RELAXED(®->hccr); |
2688 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 2731 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
2689 | } | 2732 | } |
2733 | if (qla2x00_check_reg_for_disconnect(vha, hccr)) | ||
2734 | goto out; | ||
2690 | queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work); | 2735 | queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work); |
2691 | 2736 | ||
2737 | out: | ||
2692 | return IRQ_HANDLED; | 2738 | return IRQ_HANDLED; |
2693 | } | 2739 | } |
2694 | 2740 | ||
@@ -2719,6 +2765,8 @@ qla24xx_msix_default(int irq, void *dev_id) | |||
2719 | vha = pci_get_drvdata(ha->pdev); | 2765 | vha = pci_get_drvdata(ha->pdev); |
2720 | do { | 2766 | do { |
2721 | stat = RD_REG_DWORD(®->host_status); | 2767 | stat = RD_REG_DWORD(®->host_status); |
2768 | if (qla2x00_check_reg_for_disconnect(vha, stat)) | ||
2769 | break; | ||
2722 | if (stat & HSRX_RISC_PAUSED) { | 2770 | if (stat & HSRX_RISC_PAUSED) { |
2723 | if (unlikely(pci_channel_offline(ha->pdev))) | 2771 | if (unlikely(pci_channel_offline(ha->pdev))) |
2724 | break; | 2772 | break; |
diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index cd3bedd00487..7c17b9277c3f 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c | |||
@@ -3017,6 +3017,8 @@ qlafx00_intr_handler(int irq, void *dev_id) | |||
3017 | vha = pci_get_drvdata(ha->pdev); | 3017 | vha = pci_get_drvdata(ha->pdev); |
3018 | for (iter = 50; iter--; clr_intr = 0) { | 3018 | for (iter = 50; iter--; clr_intr = 0) { |
3019 | stat = QLAFX00_RD_INTR_REG(ha); | 3019 | stat = QLAFX00_RD_INTR_REG(ha); |
3020 | if (qla2x00_check_reg_for_disconnect(vha, stat)) | ||
3021 | break; | ||
3020 | if ((stat & QLAFX00_HST_INT_STS_BITS) == 0) | 3022 | if ((stat & QLAFX00_HST_INT_STS_BITS) == 0) |
3021 | break; | 3023 | break; |
3022 | 3024 | ||
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index 11ce53dcbe7e..3da237209f85 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c | |||
@@ -2096,6 +2096,7 @@ qla82xx_msix_default(int irq, void *dev_id) | |||
2096 | int status = 0; | 2096 | int status = 0; |
2097 | unsigned long flags; | 2097 | unsigned long flags; |
2098 | uint32_t stat = 0; | 2098 | uint32_t stat = 0; |
2099 | uint32_t host_int = 0; | ||
2099 | uint16_t mb[4]; | 2100 | uint16_t mb[4]; |
2100 | 2101 | ||
2101 | rsp = (struct rsp_que *) dev_id; | 2102 | rsp = (struct rsp_que *) dev_id; |
@@ -2111,7 +2112,10 @@ qla82xx_msix_default(int irq, void *dev_id) | |||
2111 | spin_lock_irqsave(&ha->hardware_lock, flags); | 2112 | spin_lock_irqsave(&ha->hardware_lock, flags); |
2112 | vha = pci_get_drvdata(ha->pdev); | 2113 | vha = pci_get_drvdata(ha->pdev); |
2113 | do { | 2114 | do { |
2114 | if (RD_REG_DWORD(®->host_int)) { | 2115 | host_int = RD_REG_DWORD(®->host_int); |
2116 | if (qla2x00_check_reg_for_disconnect(vha, host_int)) | ||
2117 | break; | ||
2118 | if (host_int) { | ||
2115 | stat = RD_REG_DWORD(®->host_status); | 2119 | stat = RD_REG_DWORD(®->host_status); |
2116 | 2120 | ||
2117 | switch (stat & 0xff) { | 2121 | switch (stat & 0xff) { |
@@ -2156,6 +2160,7 @@ qla82xx_msix_rsp_q(int irq, void *dev_id) | |||
2156 | struct rsp_que *rsp; | 2160 | struct rsp_que *rsp; |
2157 | struct device_reg_82xx __iomem *reg; | 2161 | struct device_reg_82xx __iomem *reg; |
2158 | unsigned long flags; | 2162 | unsigned long flags; |
2163 | uint32_t host_int = 0; | ||
2159 | 2164 | ||
2160 | rsp = (struct rsp_que *) dev_id; | 2165 | rsp = (struct rsp_que *) dev_id; |
2161 | if (!rsp) { | 2166 | if (!rsp) { |
@@ -2168,8 +2173,12 @@ qla82xx_msix_rsp_q(int irq, void *dev_id) | |||
2168 | reg = &ha->iobase->isp82; | 2173 | reg = &ha->iobase->isp82; |
2169 | spin_lock_irqsave(&ha->hardware_lock, flags); | 2174 | spin_lock_irqsave(&ha->hardware_lock, flags); |
2170 | vha = pci_get_drvdata(ha->pdev); | 2175 | vha = pci_get_drvdata(ha->pdev); |
2176 | host_int = RD_REG_DWORD(®->host_int); | ||
2177 | if (qla2x00_check_reg_for_disconnect(vha, host_int)) | ||
2178 | goto out; | ||
2171 | qla24xx_process_response_queue(vha, rsp); | 2179 | qla24xx_process_response_queue(vha, rsp); |
2172 | WRT_REG_DWORD(®->host_int, 0); | 2180 | WRT_REG_DWORD(®->host_int, 0); |
2181 | out: | ||
2173 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 2182 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
2174 | return IRQ_HANDLED; | 2183 | return IRQ_HANDLED; |
2175 | } | 2184 | } |
@@ -2183,6 +2192,7 @@ qla82xx_poll(int irq, void *dev_id) | |||
2183 | struct device_reg_82xx __iomem *reg; | 2192 | struct device_reg_82xx __iomem *reg; |
2184 | int status = 0; | 2193 | int status = 0; |
2185 | uint32_t stat; | 2194 | uint32_t stat; |
2195 | uint32_t host_int = 0; | ||
2186 | uint16_t mb[4]; | 2196 | uint16_t mb[4]; |
2187 | unsigned long flags; | 2197 | unsigned long flags; |
2188 | 2198 | ||
@@ -2198,7 +2208,10 @@ qla82xx_poll(int irq, void *dev_id) | |||
2198 | spin_lock_irqsave(&ha->hardware_lock, flags); | 2208 | spin_lock_irqsave(&ha->hardware_lock, flags); |
2199 | vha = pci_get_drvdata(ha->pdev); | 2209 | vha = pci_get_drvdata(ha->pdev); |
2200 | 2210 | ||
2201 | if (RD_REG_DWORD(®->host_int)) { | 2211 | host_int = RD_REG_DWORD(®->host_int); |
2212 | if (qla2x00_check_reg_for_disconnect(vha, host_int)) | ||
2213 | goto out; | ||
2214 | if (host_int) { | ||
2202 | stat = RD_REG_DWORD(®->host_status); | 2215 | stat = RD_REG_DWORD(®->host_status); |
2203 | switch (stat & 0xff) { | 2216 | switch (stat & 0xff) { |
2204 | case 0x1: | 2217 | case 0x1: |
@@ -2226,6 +2239,7 @@ qla82xx_poll(int irq, void *dev_id) | |||
2226 | } | 2239 | } |
2227 | } | 2240 | } |
2228 | WRT_REG_DWORD(®->host_int, 0); | 2241 | WRT_REG_DWORD(®->host_int, 0); |
2242 | out: | ||
2229 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 2243 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
2230 | } | 2244 | } |
2231 | 2245 | ||
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index c9e4372698a2..690b626df930 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c | |||
@@ -2738,6 +2738,8 @@ que_init: | |||
2738 | */ | 2738 | */ |
2739 | qla2xxx_wake_dpc(base_vha); | 2739 | qla2xxx_wake_dpc(base_vha); |
2740 | 2740 | ||
2741 | INIT_WORK(&ha->board_disable, qla2x00_disable_board_on_pci_error); | ||
2742 | |||
2741 | if (IS_QLA8031(ha) || IS_MCTP_CAPABLE(ha)) { | 2743 | if (IS_QLA8031(ha) || IS_MCTP_CAPABLE(ha)) { |
2742 | sprintf(wq_name, "qla2xxx_%lu_dpc_lp_wq", base_vha->host_no); | 2744 | sprintf(wq_name, "qla2xxx_%lu_dpc_lp_wq", base_vha->host_no); |
2743 | ha->dpc_lp_wq = create_singlethread_workqueue(wq_name); | 2745 | ha->dpc_lp_wq = create_singlethread_workqueue(wq_name); |
@@ -4673,6 +4675,66 @@ exit: | |||
4673 | return rval; | 4675 | return rval; |
4674 | } | 4676 | } |
4675 | 4677 | ||
4678 | void | ||
4679 | qla2x00_disable_board_on_pci_error(struct work_struct *work) | ||
4680 | { | ||
4681 | struct qla_hw_data *ha = container_of(work, struct qla_hw_data, | ||
4682 | board_disable); | ||
4683 | struct pci_dev *pdev = ha->pdev; | ||
4684 | scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); | ||
4685 | |||
4686 | ql_log(ql_log_warn, base_vha, 0x015b, | ||
4687 | "Disabling adapter.\n"); | ||
4688 | |||
4689 | set_bit(UNLOADING, &base_vha->dpc_flags); | ||
4690 | |||
4691 | qla2x00_delete_all_vps(ha, base_vha); | ||
4692 | |||
4693 | qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); | ||
4694 | |||
4695 | qla2x00_dfs_remove(base_vha); | ||
4696 | |||
4697 | qla84xx_put_chip(base_vha); | ||
4698 | |||
4699 | if (base_vha->timer_active) | ||
4700 | qla2x00_stop_timer(base_vha); | ||
4701 | |||
4702 | base_vha->flags.online = 0; | ||
4703 | |||
4704 | qla2x00_destroy_deferred_work(ha); | ||
4705 | |||
4706 | /* | ||
4707 | * Do not try to stop beacon blink as it will issue a mailbox | ||
4708 | * command. | ||
4709 | */ | ||
4710 | qla2x00_free_sysfs_attr(base_vha, false); | ||
4711 | |||
4712 | fc_remove_host(base_vha->host); | ||
4713 | |||
4714 | scsi_remove_host(base_vha->host); | ||
4715 | |||
4716 | base_vha->flags.init_done = 0; | ||
4717 | qla25xx_delete_queues(base_vha); | ||
4718 | qla2x00_free_irqs(base_vha); | ||
4719 | qla2x00_free_fcports(base_vha); | ||
4720 | qla2x00_mem_free(ha); | ||
4721 | qla82xx_md_free(base_vha); | ||
4722 | qla2x00_free_queues(ha); | ||
4723 | |||
4724 | scsi_host_put(base_vha->host); | ||
4725 | |||
4726 | qla2x00_unmap_iobases(ha); | ||
4727 | |||
4728 | pci_release_selected_regions(ha->pdev, ha->bars); | ||
4729 | kfree(ha); | ||
4730 | ha = NULL; | ||
4731 | |||
4732 | pci_disable_pcie_error_reporting(pdev); | ||
4733 | pci_disable_device(pdev); | ||
4734 | pci_set_drvdata(pdev, NULL); | ||
4735 | |||
4736 | } | ||
4737 | |||
4676 | /************************************************************************** | 4738 | /************************************************************************** |
4677 | * qla2x00_do_dpc | 4739 | * qla2x00_do_dpc |
4678 | * This kernel thread is a task that is schedule by the interrupt handler | 4740 | * This kernel thread is a task that is schedule by the interrupt handler |
@@ -5026,9 +5088,20 @@ qla2x00_timer(scsi_qla_host_t *vha) | |||
5026 | return; | 5088 | return; |
5027 | } | 5089 | } |
5028 | 5090 | ||
5029 | /* Hardware read to raise pending EEH errors during mailbox waits. */ | 5091 | /* |
5030 | if (!pci_channel_offline(ha->pdev)) | 5092 | * Hardware read to raise pending EEH errors during mailbox waits. If |
5093 | * the read returns -1 then disable the board. | ||
5094 | */ | ||
5095 | if (!pci_channel_offline(ha->pdev)) { | ||
5031 | pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); | 5096 | pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); |
5097 | if (w == 0xffff) | ||
5098 | /* | ||
5099 | * Schedule this on the default system workqueue so that | ||
5100 | * all the adapter workqueues and the DPC thread can be | ||
5101 | * shutdown cleanly. | ||
5102 | */ | ||
5103 | schedule_work(&ha->board_disable); | ||
5104 | } | ||
5032 | 5105 | ||
5033 | /* Make sure qla82xx_watchdog is run only for physical port */ | 5106 | /* Make sure qla82xx_watchdog is run only for physical port */ |
5034 | if (!vha->vp_idx && IS_P3P_TYPE(ha)) { | 5107 | if (!vha->vp_idx && IS_P3P_TYPE(ha)) { |