Skip to content

Commit

Permalink
[SCSI] qla4xxx: Capture minidump for ISP82XX on firmware failure
Browse files Browse the repository at this point in the history
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: Tej Parkash <tej.parkash@qlogic.com>
Signed-off-by: Shyam Sundar <shyam.sundar@qlogic.com>
Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
  • Loading branch information
Tej Parkash authored and James Bottomley committed May 30, 2012
1 parent f7b4aa6 commit 068237c
Show file tree
Hide file tree
Showing 9 changed files with 1,293 additions and 16 deletions.
134 changes: 134 additions & 0 deletions drivers/scsi/qla4xxx/ql4_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 22 additions & 0 deletions drivers/scsi/qla4xxx/ql4_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 changes: 28 additions & 0 deletions drivers/scsi/qla4xxx/ql4_fw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 changes: 8 additions & 0 deletions drivers/scsi/qla4xxx/ql4_glbl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 92 additions & 0 deletions drivers/scsi/qla4xxx/ql4_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
Loading

0 comments on commit 068237c

Please sign in to comment.