Commit f7a0ed47 authored by Quinn Tran's avatar Quinn Tran Committed by Martin K. Petersen
Browse files

scsi: qla2xxx: Fix crash in PCIe error handling

BUG: unable to handle kernel NULL pointer dereference at (null)
IP: qla2x00_abort_isp+0x21/0x6b0 [qla2xxx] PGD 0 P4D 0
Oops: 0000 [#1] SMP PTI
CPU: 0 PID: 1715 Comm: kworker/0:2
Tainted: GOE 4.12.14-122.37-default #1 SLE12-SP5
Hardware name: HPE Superdome Flex/Superdome Flex, BIOS
Bundle:3.30.100 SFW:IP147.007.004.017.000.2009211957 09/21/2020
Workqueue: events aer_recover_work_func
task: ffff9e399c14ca80 task.stack: ffffc1c58e4ac000
RIP: 0010:qla2x00_abort_isp+0x21/0x6b0 [qla2xxx]
RSP: 0018:ffffc1c58e4afd50 EFLAGS: 00010282
RAX: 0000000000000000 RBX: ffff9e419cdef480 RCX: 0000000000000000
RDX: ffff9e399c14ca80 RSI: 0000000000000246 RDI: ffff9e419bbc27b8
RBP: ffff9e419bbc27b8 R08: 0000000000000004 R09: 00000000a0440000
R10: 0000000000000000 R11: ffff9e399416d1a0 R12: ffff9e419cdef000
R13: ffff9e3a7cfae800 R14: ffff9e3a7cfae800 R15: 00000000000000c0
FS:  0000000000000000(0000) GS:ffff9e39a0000000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 00000006cd00a005 CR4: 00000000007606f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
PKRU: 55555554
Call Trace:
  qla2xxx_pci_slot_reset+0x141/0x160 [qla2xxx]
  report_slot_reset+0x41/0x80
  ? merge_result.part.4+0x30/0x30
  pci_walk_bus+0x70/0x90
  pcie_do_recovery+0x1db/0x2e0
  aer_recover_work_func+0xc2/0xf0
  process_one_work+0x14c/0x390

Disable board_disable logic where driver resources are freed while OS is in
the process of recovering the adapter.

Link: https://lore.kernel.org/r/20210329085229.4367-9-njavali@marvell.com


Tested-by: default avatarLaurence Oberman <loberman@redhat.com>
Signed-off-by: default avatarQuinn Tran <qutran@marvell.com>
Signed-off-by: default avatarNilesh Javali <njavali@marvell.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 610d027b
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -113,8 +113,13 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
	uint32_t stat;
	ulong i, j, timer = 6000000;
	int rval = QLA_FUNCTION_FAILED;
	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);

	clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);

	if (qla_pci_disconnected(vha, reg))
		return rval;

	for (i = 0; i < ram_dwords; i += dwords, addr += dwords) {
		if (i + dwords > ram_dwords)
			dwords = ram_dwords - i;
@@ -138,6 +143,9 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
		while (timer--) {
			udelay(5);

			if (qla_pci_disconnected(vha, reg))
				return rval;

			stat = rd_reg_dword(&reg->host_status);
			/* Check for pending interrupts. */
			if (!(stat & HSRX_RISC_INT))
@@ -192,9 +200,13 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, __be32 *ram,
	uint32_t dwords = qla2x00_gid_list_size(ha) / 4;
	uint32_t stat;
	ulong i, j, timer = 6000000;
	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);

	clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);

	if (qla_pci_disconnected(vha, reg))
		return rval;

	for (i = 0; i < ram_dwords; i += dwords, addr += dwords) {
		if (i + dwords > ram_dwords)
			dwords = ram_dwords - i;
@@ -216,8 +228,10 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, __be32 *ram,
		ha->flags.mbox_int = 0;
		while (timer--) {
			udelay(5);
			stat = rd_reg_dword(&reg->host_status);
			if (qla_pci_disconnected(vha, reg))
				return rval;

			stat = rd_reg_dword(&reg->host_status);
			/* Check for pending interrupts. */
			if (!(stat & HSRX_RISC_INT))
				continue;
+10 −0
Original line number Diff line number Diff line
@@ -396,6 +396,7 @@ typedef union {
	} b;
} port_id_t;
#define INVALID_PORT_ID	0xFFFFFF
#define ISP_REG16_DISCONNECT 0xFFFF

static inline le_id_t be_id_to_le(be_id_t id)
{
@@ -3857,6 +3858,13 @@ struct qla_hw_data_stat {
	u32 num_mpi_reset;
};

/* refer to pcie_do_recovery reference */
typedef enum {
	QLA_PCI_RESUME,
	QLA_PCI_ERR_DETECTED,
	QLA_PCI_MMIO_ENABLED,
	QLA_PCI_SLOT_RESET,
} pci_error_state_t;
/*
 * Qlogic host adapter specific data structure.
*/
@@ -4607,6 +4615,7 @@ struct qla_hw_data {
#define DEFAULT_ZIO_THRESHOLD 5

	struct qla_hw_data_stat stat;
	pci_error_state_t pci_error_state;
};

struct active_regions {
@@ -4727,6 +4736,7 @@ typedef struct scsi_qla_host {
#define FX00_CRITEMP_RECOVERY	25
#define FX00_HOST_INFO_RESEND	26
#define QPAIR_ONLINE_CHECK_NEEDED	27
#define DO_EEH_RECOVERY		28
#define DETECT_SFP_CHANGE	29
#define N2N_LOGIN_NEEDED	30
#define IOCB_WORK_ACTIVE	31
+3 −0
Original line number Diff line number Diff line
@@ -224,6 +224,7 @@ 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 *);
extern void qla_eeh_work(struct work_struct *);
extern void qla2x00_sp_compl(srb_t *sp, int);
extern void qla2xxx_qpair_sp_free_dma(srb_t *sp);
extern void qla2xxx_qpair_sp_compl(srb_t *sp, int);
@@ -235,6 +236,8 @@ int qla24xx_post_relogin_work(struct scsi_qla_host *vha);
void qla2x00_wait_for_sess_deletion(scsi_qla_host_t *);
void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
			       struct purex_item *pkt);
void qla_pci_set_eeh_busy(struct scsi_qla_host *);
void qla_schedule_eeh_work(struct scsi_qla_host *);

/*
 * Global Functions in qla_mid.c source file.
+26 −14
Original line number Diff line number Diff line
@@ -6932,22 +6932,18 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
	}
	spin_unlock_irqrestore(&ha->vport_slock, flags);

	if (!ha->flags.eeh_busy) {
	/* Make sure for ISP 82XX IO DMA is complete */
	if (IS_P3P_TYPE(ha)) {
		qla82xx_chip_reset_cleanup(vha);
		ql_log(ql_log_info, vha, 0x00b4,
		       "Done chip reset cleanup.\n");

			/* Done waiting for pending commands.
			 * Reset the online flag.
			 */
		/* Done waiting for pending commands. Reset online flag */
		vha->flags.online = 0;
	}

	/* Requeue all commands in outstanding command list. */
	qla2x00_abort_all_cmds(vha, DID_RESET << 16);
	}
	/* memory barrier */
	wmb();
}
@@ -6978,6 +6974,12 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
		if (vha->hw->flags.port_isolated)
			return status;

		if (qla2x00_isp_reg_stat(ha)) {
			ql_log(ql_log_info, vha, 0x803f,
			       "ISP Abort - ISP reg disconnect, exiting.\n");
			return status;
		}

		if (test_and_clear_bit(ISP_ABORT_TO_ROM, &vha->dpc_flags)) {
			ha->flags.chip_reset_done = 1;
			vha->flags.online = 1;
@@ -7017,8 +7019,18 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)

		ha->isp_ops->get_flash_version(vha, req->ring);

		if (qla2x00_isp_reg_stat(ha)) {
			ql_log(ql_log_info, vha, 0x803f,
			       "ISP Abort - ISP reg disconnect pre nvram config, exiting.\n");
			return status;
		}
		ha->isp_ops->nvram_config(vha);

		if (qla2x00_isp_reg_stat(ha)) {
			ql_log(ql_log_info, vha, 0x803f,
			       "ISP Abort - ISP reg disconnect post nvmram config, exiting.\n");
			return status;
		}
		if (!qla2x00_restart_isp(vha)) {
			clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);

+46 −0
Original line number Diff line number Diff line
@@ -432,3 +432,49 @@ qla_put_iocbs(struct qla_qpair *qp, struct iocb_resource *iores)
	}
	iores->res_type = RESOURCE_NONE;
}

#define ISP_REG_DISCONNECT 0xffffffffU
/**************************************************************************
 * qla2x00_isp_reg_stat
 *
 * Description:
 *        Read the host status register of ISP before aborting the command.
 *
 * Input:
 *       ha = pointer to host adapter structure.
 *
 *
 * Returns:
 *       Either true or false.
 *
 * Note: Return true if there is register disconnect.
 **************************************************************************/
static inline
uint32_t qla2x00_isp_reg_stat(struct qla_hw_data *ha)
{
	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
	struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;

	if (IS_P3P_TYPE(ha))
		return ((rd_reg_dword(&reg82->host_int)) == ISP_REG_DISCONNECT);
	else
		return ((rd_reg_dword(&reg->host_status)) ==
			ISP_REG_DISCONNECT);
}

static inline
bool qla_pci_disconnected(struct scsi_qla_host *vha,
			  struct device_reg_24xx __iomem *reg)
{
	uint32_t stat;
	bool ret = false;

	stat = rd_reg_dword(&reg->host_status);
	if (stat == 0xffffffff) {
		ql_log(ql_log_info, vha, 0x8041,
		       "detected PCI disconnect.\n");
		qla_schedule_eeh_work(vha);
		ret = true;
	}
	return ret;
}
Loading