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

Commit 068237c8 authored by Tej Parkash's avatar Tej Parkash Committed by James Bottomley
Browse files

[SCSI] qla4xxx: Capture minidump for ISP82XX on firmware failure



Added support to capture dump (Minidump) which allows us to
catpure a snapshot of the firmware/hardware states at the time
of firmware failure

Signed-off-by: default avatarTej Parkash <tej.parkash@qlogic.com>
Signed-off-by: default avatarShyam Sundar <shyam.sundar@qlogic.com>
Signed-off-by: default avatarVikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent f7b4aa63
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
@@ -9,6 +9,140 @@
#include "ql4_glbl.h"
#include "ql4_dbg.h"

static ssize_t
qla4_8xxx_sysfs_read_fw_dump(struct file *filep, struct kobject *kobj,
			     struct bin_attribute *ba, char *buf, loff_t off,
			     size_t count)
{
	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
					       struct device, kobj)));

	if (!is_qla8022(ha))
		return -EINVAL;

	if (!test_bit(AF_82XX_DUMP_READING, &ha->flags))
		return 0;

	return memory_read_from_buffer(buf, count, &off, ha->fw_dump,
				       ha->fw_dump_size);
}

static ssize_t
qla4_8xxx_sysfs_write_fw_dump(struct file *filep, struct kobject *kobj,
			      struct bin_attribute *ba, char *buf, loff_t off,
			      size_t count)
{
	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
					       struct device, kobj)));
	uint32_t dev_state;
	long reading;
	int ret = 0;

	if (!is_qla8022(ha))
		return -EINVAL;

	if (off != 0)
		return ret;

	buf[1] = 0;
	ret = kstrtol(buf, 10, &reading);
	if (ret) {
		ql4_printk(KERN_ERR, ha, "%s: Invalid input. Return err %d\n",
			   __func__, ret);
		return ret;
	}

	switch (reading) {
	case 0:
		/* clear dump collection flags */
		if (test_and_clear_bit(AF_82XX_DUMP_READING, &ha->flags)) {
			clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
			/* Reload minidump template */
			qla4xxx_alloc_fw_dump(ha);
			DEBUG2(ql4_printk(KERN_INFO, ha,
					  "Firmware template reloaded\n"));
		}
		break;
	case 1:
		/* Set flag to read dump */
		if (test_bit(AF_82XX_FW_DUMPED, &ha->flags) &&
		    !test_bit(AF_82XX_DUMP_READING, &ha->flags)) {
			set_bit(AF_82XX_DUMP_READING, &ha->flags);
			DEBUG2(ql4_printk(KERN_INFO, ha,
					  "Raw firmware dump ready for read on (%ld).\n",
					  ha->host_no));
		}
		break;
	case 2:
		/* Reset HBA */
		qla4_8xxx_idc_lock(ha);
		dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
		if (dev_state == QLA82XX_DEV_READY) {
			ql4_printk(KERN_INFO, ha,
				   "%s: Setting Need reset, reset_owner is 0x%x.\n",
				   __func__, ha->func_num);
			qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
					QLA82XX_DEV_NEED_RESET);
			set_bit(AF_82XX_RST_OWNER, &ha->flags);
		} else
			ql4_printk(KERN_INFO, ha,
				   "%s: Reset not performed as device state is 0x%x\n",
				   __func__, dev_state);

		qla4_8xxx_idc_unlock(ha);
		break;
	default:
		/* do nothing */
		break;
	}

	return count;
}

static struct bin_attribute sysfs_fw_dump_attr = {
	.attr = {
		.name = "fw_dump",
		.mode = S_IRUSR | S_IWUSR,
	},
	.size = 0,
	.read = qla4_8xxx_sysfs_read_fw_dump,
	.write = qla4_8xxx_sysfs_write_fw_dump,
};

static struct sysfs_entry {
	char *name;
	struct bin_attribute *attr;
} bin_file_entries[] = {
	{ "fw_dump", &sysfs_fw_dump_attr },
	{ NULL },
};

void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha)
{
	struct Scsi_Host *host = ha->host;
	struct sysfs_entry *iter;
	int ret;

	for (iter = bin_file_entries; iter->name; iter++) {
		ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
					    iter->attr);
		if (ret)
			ql4_printk(KERN_ERR, ha,
				   "Unable to create sysfs %s binary attribute (%d).\n",
				   iter->name, ret);
	}
}

void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha)
{
	struct Scsi_Host *host = ha->host;
	struct sysfs_entry *iter;

	for (iter = bin_file_entries; iter->name; iter++)
		sysfs_remove_bin_file(&host->shost_gendev.kobj,
				      iter->attr);
}

/* Scsi_Host attributes. */
static ssize_t
qla4xxx_fw_version_show(struct device *dev,
+22 −0
Original line number Diff line number Diff line
@@ -398,6 +398,16 @@ struct isp_operations {
	int (*get_sys_info) (struct scsi_qla_host *);
};

struct ql4_mdump_size_table {
	uint32_t size;
	uint32_t size_cmask_02;
	uint32_t size_cmask_04;
	uint32_t size_cmask_08;
	uint32_t size_cmask_10;
	uint32_t size_cmask_FF;
	uint32_t version;
};

/*qla4xxx ipaddress configuration details */
struct ipaddress_config {
	uint16_t ipv4_options;
@@ -485,6 +495,10 @@ struct scsi_qla_host {
#define AF_EEH_BUSY			20 /* 0x00100000 */
#define AF_PCI_CHANNEL_IO_PERM_FAILURE	21 /* 0x00200000 */
#define AF_BUILD_DDB_LIST		22 /* 0x00400000 */
#define AF_82XX_FW_DUMPED		24 /* 0x01000000 */
#define AF_82XX_RST_OWNER		25 /* 0x02000000 */
#define AF_82XX_DUMP_READING		26 /* 0x04000000 */

	unsigned long dpc_flags;

#define DPC_RESET_HA			1 /* 0x00000002 */
@@ -662,6 +676,11 @@ struct scsi_qla_host {

	uint32_t nx_dev_init_timeout;
	uint32_t nx_reset_timeout;
	void *fw_dump;
	uint32_t fw_dump_size;
	uint32_t fw_dump_capture_mask;
	void *fw_dump_tmplt_hdr;
	uint32_t fw_dump_tmplt_size;

	struct completion mbx_intr_comp;

@@ -936,4 +955,7 @@ static inline int ql4xxx_reset_active(struct scsi_qla_host *ha)
#define PROCESS_ALL_AENS	 0
#define FLUSH_DDB_CHANGED_AENS	 1

/* Defines for udev events */
#define QL4_UEVENT_CODE_FW_DUMP		0

#endif	/*_QLA4XXX_H */
+28 −0
Original line number Diff line number Diff line
@@ -385,6 +385,11 @@ struct qla_flt_region {
#define MBOX_CMD_GET_IP_ADDR_STATE		0x0091
#define MBOX_CMD_SEND_IPV6_ROUTER_SOL		0x0092
#define MBOX_CMD_GET_DB_ENTRY_CURRENT_IP_ADDR	0x0093
#define MBOX_CMD_MINIDUMP			0x0129

/* Minidump subcommand */
#define MINIDUMP_GET_SIZE_SUBCOMMAND		0x00
#define MINIDUMP_GET_TMPLT_SUBCOMMAND		0x01

/* Mailbox 1 */
#define FW_STATE_READY				0x0000
@@ -1190,4 +1195,27 @@ struct ql_iscsi_stats {
	uint8_t reserved2[264]; /* 0x0308 - 0x040F */
};

#define QLA82XX_DBG_STATE_ARRAY_LEN		16
#define QLA82XX_DBG_CAP_SIZE_ARRAY_LEN		8
#define QLA82XX_DBG_RSVD_ARRAY_LEN		8

struct qla4_8xxx_minidump_template_hdr {
	uint32_t entry_type;
	uint32_t first_entry_offset;
	uint32_t size_of_template;
	uint32_t capture_debug_level;
	uint32_t num_of_entries;
	uint32_t version;
	uint32_t driver_timestamp;
	uint32_t checksum;

	uint32_t driver_capture_mask;
	uint32_t driver_info_word2;
	uint32_t driver_info_word3;
	uint32_t driver_info_word4;

	uint32_t saved_state_array[QLA82XX_DBG_STATE_ARRAY_LEN];
	uint32_t capture_size_array[QLA82XX_DBG_CAP_SIZE_ARRAY_LEN];
};

#endif /*  _QLA4X_FW_H */
+8 −0
Original line number Diff line number Diff line
@@ -196,10 +196,18 @@ int qla4xxx_bsg_request(struct bsg_job *bsg_job);
int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job);

void qla4xxx_arm_relogin_timer(struct ddb_entry *ddb_entry);
int qla4xxx_get_minidump_template(struct scsi_qla_host *ha,
				  dma_addr_t phys_addr);
int qla4xxx_req_template_size(struct scsi_qla_host *ha);
void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha);
void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha);
void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha);

extern int ql4xextended_error_logging;
extern int ql4xdontresethba;
extern int ql4xenablemsix;
extern int ql4xmdcapmask;
extern int ql4xenablemd;

extern struct device_attribute *qla4xxx_host_attrs[];
#endif /* _QLA4x_GBL_H */
+92 −0
Original line number Diff line number Diff line
@@ -277,6 +277,94 @@ qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha)
	return ipv4_wait|ipv6_wait;
}

/**
 * qla4xxx_alloc_fw_dump - Allocate memory for minidump data.
 * @ha: pointer to host adapter structure.
 **/
void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha)
{
	int status;
	uint32_t capture_debug_level;
	int hdr_entry_bit, k;
	void *md_tmp;
	dma_addr_t md_tmp_dma;
	struct qla4_8xxx_minidump_template_hdr *md_hdr;

	if (ha->fw_dump) {
		ql4_printk(KERN_WARNING, ha,
			   "Firmware dump previously allocated.\n");
		return;
	}

	status = qla4xxx_req_template_size(ha);
	if (status != QLA_SUCCESS) {
		ql4_printk(KERN_INFO, ha,
			   "scsi%ld: Failed to get template size\n",
			   ha->host_no);
		return;
	}

	clear_bit(AF_82XX_FW_DUMPED, &ha->flags);

	/* Allocate memory for saving the template */
	md_tmp = dma_alloc_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
				    &md_tmp_dma, GFP_KERNEL);

	/* Request template */
	status =  qla4xxx_get_minidump_template(ha, md_tmp_dma);
	if (status != QLA_SUCCESS) {
		ql4_printk(KERN_INFO, ha,
			   "scsi%ld: Failed to get minidump template\n",
			   ha->host_no);
		goto alloc_cleanup;
	}

	md_hdr = (struct qla4_8xxx_minidump_template_hdr *)md_tmp;

	capture_debug_level = md_hdr->capture_debug_level;

	/* Get capture mask based on module loadtime setting. */
	if (ql4xmdcapmask >= 0x3 && ql4xmdcapmask <= 0x7F)
		ha->fw_dump_capture_mask = ql4xmdcapmask;
	else
		ha->fw_dump_capture_mask = capture_debug_level;

	md_hdr->driver_capture_mask = ha->fw_dump_capture_mask;

	DEBUG2(ql4_printk(KERN_INFO, ha, "Minimum num of entries = %d\n",
			  md_hdr->num_of_entries));
	DEBUG2(ql4_printk(KERN_INFO, ha, "Dump template size  = %d\n",
			  ha->fw_dump_tmplt_size));
	DEBUG2(ql4_printk(KERN_INFO, ha, "Selected Capture mask =0x%x\n",
			  ha->fw_dump_capture_mask));

	/* Calculate fw_dump_size */
	for (hdr_entry_bit = 0x2, k = 1; (hdr_entry_bit & 0xFF);
	     hdr_entry_bit <<= 1, k++) {
		if (hdr_entry_bit & ha->fw_dump_capture_mask)
			ha->fw_dump_size += md_hdr->capture_size_array[k];
	}

	/* Total firmware dump size including command header */
	ha->fw_dump_size += ha->fw_dump_tmplt_size;
	ha->fw_dump = vmalloc(ha->fw_dump_size);
	if (!ha->fw_dump)
		goto alloc_cleanup;

	DEBUG2(ql4_printk(KERN_INFO, ha,
			  "Minidump Tempalate Size = 0x%x KB\n",
			  ha->fw_dump_tmplt_size));
	DEBUG2(ql4_printk(KERN_INFO, ha,
			  "Total Minidump size = 0x%x KB\n", ha->fw_dump_size));

	memcpy(ha->fw_dump, md_tmp, ha->fw_dump_tmplt_size);
	ha->fw_dump_tmplt_hdr = ha->fw_dump;

alloc_cleanup:
	dma_free_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
			  md_tmp, md_tmp_dma);
}

static int qla4xxx_fw_ready(struct scsi_qla_host *ha)
{
	uint32_t timeout_count;
@@ -445,9 +533,13 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha)
			      "control block\n", ha->host_no, __func__));
		return status;
	}

	if (!qla4xxx_fw_ready(ha))
		return status;

	if (is_qla8022(ha) && !test_bit(AF_INIT_DONE, &ha->flags))
		qla4xxx_alloc_fw_dump(ha);

	return qla4xxx_get_firmware_status(ha);
}

Loading