Commit dba2cc03 authored by Deepak Ukey's avatar Deepak Ukey Committed by Martin K. Petersen

scsi: pm80xx: sysfs attribute for non fatal dump

Added the sysfs attribute for non fatal log so that management utility can
get the non fatal dump from driver. The non-fatal error is an error
condition or abnormal behavior detected by the host, or detected and
reported by the controller to the host.The non-fatal error does not stop
the controller firmware and enables it to still respond to host requests.
A typical example of a non-fatal error is an I/O timeout or an unusual
error notification from the controller. Since the firmware is operational,
the error dump information is pushed to host memory (by firmware) upon
request from the host.

Link: https://lore.kernel.org/r/20200316074906.9119-6-deepak.ukey@microchip.comAcked-by: default avatarJack Wang <jinpu.wang@cloud.ionos.com>
Signed-off-by: default avatarDeepak Ukey <deepak.ukey@microchip.com>
Signed-off-by: default avatarViswas G <Viswas.G@microchip.com>
Signed-off-by: default avatarRadha Ramachandran <radha@google.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent b40f2882
......@@ -554,6 +554,49 @@ static ssize_t pm8001_ctl_fatal_log_show(struct device *cdev,
static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL);
/**
** non_fatal_log_show - non fatal error logging
** @cdev:pointer to embedded class device
** @buf: the buffer returned
**
** A sysfs 'read-only' shost attribute.
**/
static ssize_t non_fatal_log_show(struct device *cdev,
struct device_attribute *attr, char *buf)
{
u32 count;
count = pm80xx_get_non_fatal_dump(cdev, attr, buf);
return count;
}
static DEVICE_ATTR_RO(non_fatal_log);
static ssize_t non_fatal_count_show(struct device *cdev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
return snprintf(buf, PAGE_SIZE, "%08x",
pm8001_ha->non_fatal_count);
}
static ssize_t non_fatal_count_store(struct device *cdev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
int val = 0;
if (kstrtoint(buf, 16, &val) != 0)
return -EINVAL;
pm8001_ha->non_fatal_count = val;
return strlen(buf);
}
static DEVICE_ATTR_RW(non_fatal_count);
/**
** pm8001_ctl_gsm_log_show - gsm dump collection
......@@ -829,6 +872,8 @@ struct device_attribute *pm8001_host_attrs[] = {
&dev_attr_aap_log,
&dev_attr_iop_log,
&dev_attr_fatal_log,
&dev_attr_non_fatal_log,
&dev_attr_non_fatal_count,
&dev_attr_gsm_log,
&dev_attr_max_out_io,
&dev_attr_max_devices,
......
......@@ -486,6 +486,7 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
pm8001_ha->shost = shost;
pm8001_ha->id = pm8001_id++;
pm8001_ha->logging_level = logging_level;
pm8001_ha->non_fatal_count = 0;
if (link_rate >= 1 && link_rate <= 15)
pm8001_ha->link_rate = (link_rate << 8);
else {
......
......@@ -558,6 +558,8 @@ struct pm8001_hba_info {
const struct firmware *fw_image;
struct isr_param irq_vector[PM8001_MAX_MSIX_VEC];
u32 reset_in_progress;
u32 non_fatal_count;
u32 non_fatal_read_length;
};
struct pm8001_work {
......@@ -741,6 +743,8 @@ void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha,
int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue);
ssize_t pm80xx_get_fatal_dump(struct device *cdev,
struct device_attribute *attr, char *buf);
ssize_t pm80xx_get_non_fatal_dump(struct device *cdev,
struct device_attribute *attr, char *buf);
ssize_t pm8001_get_gsm_dump(struct device *cdev, u32, char *buf);
/* ctl shared API */
extern struct device_attribute *pm8001_host_attrs[];
......
......@@ -393,6 +393,136 @@ ssize_t pm80xx_get_fatal_dump(struct device *cdev,
(char *)buf;
}
/* pm80xx_get_non_fatal_dump - dump the nonfatal data from the dma
* location by the firmware.
*/
ssize_t pm80xx_get_non_fatal_dump(struct device *cdev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
void __iomem *nonfatal_table_address = pm8001_ha->fatal_tbl_addr;
u32 accum_len = 0;
u32 total_len = 0;
u32 reg_val = 0;
u32 *temp = NULL;
u32 index = 0;
u32 output_length;
unsigned long start = 0;
char *buf_copy = buf;
temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr;
if (++pm8001_ha->non_fatal_count == 1) {
if (pm8001_ha->chip_id == chip_8001) {
snprintf(pm8001_ha->forensic_info.data_buf.direct_data,
PAGE_SIZE, "Not supported for SPC controller");
return 0;
}
PM8001_IO_DBG(pm8001_ha,
pm8001_printk("forensic_info TYPE_NON_FATAL...\n"));
/*
* Step 1: Write the host buffer parameters in the MPI Fatal and
* Non-Fatal Error Dump Capture Table.This is the buffer
* where debug data will be DMAed to.
*/
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_LO_OFFSET,
pm8001_ha->memoryMap.region[FORENSIC_MEM].phys_addr_lo);
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_HI_OFFSET,
pm8001_ha->memoryMap.region[FORENSIC_MEM].phys_addr_hi);
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_LENGTH, SYSFS_OFFSET);
/* Optionally, set the DUMPCTRL bit to 1 if the host
* keeps sending active I/Os while capturing the non-fatal
* debug data. Otherwise, leave this bit set to zero
*/
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_HANDSHAKE, MPI_FATAL_EDUMP_HANDSHAKE_RDY);
/*
* Step 2: Clear Accumulative Length of Debug Data Transferred
* [ACCDDLEN] field in the MPI Fatal and Non-Fatal Error Dump
* Capture Table to zero.
*/
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_ACCUM_LEN, 0);
/* initiallize previous accumulated length to 0 */
pm8001_ha->forensic_preserved_accumulated_transfer = 0;
pm8001_ha->non_fatal_read_length = 0;
}
total_len = pm8001_mr32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_TOTAL_LEN);
/*
* Step 3:Clear Fatal/Non-Fatal Debug Data Transfer Status [FDDTSTAT]
* field and then request that the SPCv controller transfer the debug
* data by setting bit 7 of the Inbound Doorbell Set Register.
*/
pm8001_mw32(nonfatal_table_address, MPI_FATAL_EDUMP_TABLE_STATUS, 0);
pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET,
SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP);
/*
* Step 4.1: Read back the Inbound Doorbell Set Register (by polling for
* 2 seconds) until register bit 7 is cleared.
* This step only indicates the request is accepted by the controller.
*/
start = jiffies + (2 * HZ); /* 2 sec */
do {
reg_val = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET) &
SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP;
} while ((reg_val != 0) && time_before(jiffies, start));
/* Step 4.2: To check the completion of the transfer, poll the Fatal/Non
* Fatal Debug Data Transfer Status [FDDTSTAT] field for 2 seconds in
* the MPI Fatal and Non-Fatal Error Dump Capture Table.
*/
start = jiffies + (2 * HZ); /* 2 sec */
do {
reg_val = pm8001_mr32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_STATUS);
} while ((!reg_val) && time_before(jiffies, start));
if ((reg_val == 0x00) ||
(reg_val == MPI_FATAL_EDUMP_TABLE_STAT_DMA_FAILED) ||
(reg_val > MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE)) {
pm8001_ha->non_fatal_read_length = 0;
buf_copy += snprintf(buf_copy, PAGE_SIZE, "%08x ", 0xFFFFFFFF);
pm8001_ha->non_fatal_count = 0;
return (buf_copy - buf);
} else if (reg_val ==
MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_MORE_DATA) {
buf_copy += snprintf(buf_copy, PAGE_SIZE, "%08x ", 2);
} else if ((reg_val == MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE) ||
(pm8001_ha->non_fatal_read_length >= total_len)) {
pm8001_ha->non_fatal_read_length = 0;
buf_copy += snprintf(buf_copy, PAGE_SIZE, "%08x ", 4);
pm8001_ha->non_fatal_count = 0;
}
accum_len = pm8001_mr32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_ACCUM_LEN);
output_length = accum_len -
pm8001_ha->forensic_preserved_accumulated_transfer;
for (index = 0; index < output_length/4; index++)
buf_copy += snprintf(buf_copy, PAGE_SIZE,
"%08x ", *(temp+index));
pm8001_ha->non_fatal_read_length += output_length;
/* store current accumulated length to use in next iteration as
* the previous accumulated length
*/
pm8001_ha->forensic_preserved_accumulated_transfer = accum_len;
return (buf_copy - buf);
}
/**
* read_main_config_table - read the configure table and save it.
* @pm8001_ha: our hba card information
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment