Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3ddac19 authored by Chad Dupuis's avatar Chad Dupuis Committed by James Bottomley
Browse files

[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: default avatarChad Dupuis <chad.dupuis@qlogic.com>
Signed-off-by: default avatarSaurav Kashyap <saurav.kashyap@qlogic.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent fe1b806f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@
 * ----------------------------------------------------------------------
 * |             Level            |   Last Value Used  |     Holes	|
 * ----------------------------------------------------------------------
 * | Module Init and Probe        |       0x015a       | 0x4b,0xba,0xfa |
 * | Module Init and Probe        |       0x015b       | 0x4b,0xba,0xfa |
 * | Mailbox commands             |       0x1181       | 0x111a-0x111b  |
 * |                              |                    | 0x1155-0x1158  |
 * |                              |                    | 0x1018-0x1019  |
+1 −0
Original line number Diff line number Diff line
@@ -3301,6 +3301,7 @@ struct qla_hw_data {
	struct work_struct nic_core_reset;
	struct work_struct idc_state_handler;
	struct work_struct nic_core_unrecoverable;
	struct work_struct board_disable;

	struct mr_data_fx00 mr;

+4 −0
Original line number Diff line number Diff line
@@ -159,6 +159,9 @@ extern int qla83xx_clear_drv_presence(scsi_qla_host_t *vha);
extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha);
extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);

extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
extern void qla2x00_disable_board_on_pci_error(struct work_struct *);

/*
 * Global Functions in qla_mid.c source file.
 */
@@ -454,6 +457,7 @@ extern uint8_t *qla25xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
extern int qla25xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
				    uint32_t);
extern int qla2x00_is_a_vp_did(scsi_qla_host_t *, uint32_t);
bool qla2x00_check_reg_for_disconnect(scsi_qla_host_t *, uint32_t);

extern int qla2x00_beacon_on(struct scsi_qla_host *);
extern int qla2x00_beacon_off(struct scsi_qla_host *);
+49 −1
Original line number Diff line number Diff line
@@ -56,6 +56,16 @@ qla2100_intr_handler(int irq, void *dev_id)
	vha = pci_get_drvdata(ha->pdev);
	for (iter = 50; iter--; ) {
		hccr = RD_REG_WORD(&reg->hccr);
		/* Check for PCI disconnection */
		if (hccr == 0xffff) {
			/*
			 * Schedule this on the default system workqueue so that
			 * all the adapter workqueues and the DPC thread can be
			 * shutdown cleanly.
			 */
			schedule_work(&ha->board_disable);
			break;
		}
		if (hccr & HCCR_RISC_PAUSE) {
			if (pci_channel_offline(ha->pdev))
				break;
@@ -110,6 +120,22 @@ qla2100_intr_handler(int irq, void *dev_id)
	return (IRQ_HANDLED);
}

bool
qla2x00_check_reg_for_disconnect(scsi_qla_host_t *vha, uint32_t reg)
{
	/* Check for PCI disconnection */
	if (reg == 0xffffffff) {
		/*
		 * Schedule this on the default system workqueue so that all the
		 * adapter workqueues and the DPC thread can be shutdown
		 * cleanly.
		 */
		schedule_work(&vha->hw->board_disable);
		return true;
	} else
		return false;
}

/**
 * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx.
 * @irq:
@@ -148,11 +174,14 @@ qla2300_intr_handler(int irq, void *dev_id)
	vha = pci_get_drvdata(ha->pdev);
	for (iter = 50; iter--; ) {
		stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
		if (qla2x00_check_reg_for_disconnect(vha, stat))
			break;
		if (stat & HSR_RISC_PAUSED) {
			if (unlikely(pci_channel_offline(ha->pdev)))
				break;

			hccr = RD_REG_WORD(&reg->hccr);

			if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8))
				ql_log(ql_log_warn, vha, 0x5026,
				    "Parity error -- HCCR=%x, Dumping "
@@ -2571,6 +2600,8 @@ qla24xx_intr_handler(int irq, void *dev_id)
	vha = pci_get_drvdata(ha->pdev);
	for (iter = 50; iter--; ) {
		stat = RD_REG_DWORD(&reg->host_status);
		if (qla2x00_check_reg_for_disconnect(vha, stat))
			break;
		if (stat & HSRX_RISC_PAUSED) {
			if (unlikely(pci_channel_offline(ha->pdev)))
				break;
@@ -2640,6 +2671,7 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
	struct device_reg_24xx __iomem *reg;
	struct scsi_qla_host *vha;
	unsigned long flags;
	uint32_t stat = 0;

	rsp = (struct rsp_que *) dev_id;
	if (!rsp) {
@@ -2653,11 +2685,19 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
	spin_lock_irqsave(&ha->hardware_lock, flags);

	vha = pci_get_drvdata(ha->pdev);
	/*
	 * Use host_status register to check to PCI disconnection before we
	 * we process the response queue.
	 */
	stat = RD_REG_DWORD(&reg->host_status);
	if (qla2x00_check_reg_for_disconnect(vha, stat))
		goto out;
	qla24xx_process_response_queue(vha, rsp);
	if (!ha->flags.disable_msix_handshake) {
		WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
		RD_REG_DWORD_RELAXED(&reg->hccr);
	}
out:
	spin_unlock_irqrestore(&ha->hardware_lock, flags);

	return IRQ_HANDLED;
@@ -2667,9 +2707,11 @@ static irqreturn_t
qla25xx_msix_rsp_q(int irq, void *dev_id)
{
	struct qla_hw_data *ha;
	scsi_qla_host_t *vha;
	struct rsp_que *rsp;
	struct device_reg_24xx __iomem *reg;
	unsigned long flags;
	uint32_t hccr = 0;

	rsp = (struct rsp_que *) dev_id;
	if (!rsp) {
@@ -2678,17 +2720,21 @@ qla25xx_msix_rsp_q(int irq, void *dev_id)
		return IRQ_NONE;
	}
	ha = rsp->hw;
	vha = pci_get_drvdata(ha->pdev);

	/* Clear the interrupt, if enabled, for this response queue */
	if (!ha->flags.disable_msix_handshake) {
		reg = &ha->iobase->isp24;
		spin_lock_irqsave(&ha->hardware_lock, flags);
		WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
		RD_REG_DWORD_RELAXED(&reg->hccr);
		hccr = RD_REG_DWORD_RELAXED(&reg->hccr);
		spin_unlock_irqrestore(&ha->hardware_lock, flags);
	}
	if (qla2x00_check_reg_for_disconnect(vha, hccr))
		goto out;
	queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work);

out:
	return IRQ_HANDLED;
}

@@ -2719,6 +2765,8 @@ qla24xx_msix_default(int irq, void *dev_id)
	vha = pci_get_drvdata(ha->pdev);
	do {
		stat = RD_REG_DWORD(&reg->host_status);
		if (qla2x00_check_reg_for_disconnect(vha, stat))
			break;
		if (stat & HSRX_RISC_PAUSED) {
			if (unlikely(pci_channel_offline(ha->pdev)))
				break;
+2 −0
Original line number Diff line number Diff line
@@ -3017,6 +3017,8 @@ qlafx00_intr_handler(int irq, void *dev_id)
	vha = pci_get_drvdata(ha->pdev);
	for (iter = 50; iter--; clr_intr = 0) {
		stat = QLAFX00_RD_INTR_REG(ha);
		if (qla2x00_check_reg_for_disconnect(vha, stat))
			break;
		if ((stat & QLAFX00_HST_INT_STS_BITS) == 0)
			break;

Loading