The pm8001 driver event log currently only reports the current firmware event log index and the first entry (if log has not wrapped). The enclosed patch enhances the event log retrieved to sequentially return all un-retrieved entries until hitting the last, from then on reporting the last entry. The format has been changed in a parseable fashion reporting some of the interpreted fields; the entry sequence number can be used to determine the boundary condition when the last entry is retrieved. Formerly, when the logging level is changed at runtime, it does not transmit the updated level to the firmware. We also added a set of insmod parameters so that the logging level may be set at initialization time. As this disrupts backward compatibility, we look forward to community comments. Also, once an earlier log entry is retrieved, it is the expectation of the logging/forensic program(?) to record them as they can not be retrieved again. Please keep in mind that the first event log entry as previously reported by the existing code becomes somewhat moot if new entries form. Signed-off-by: mark_salyzyn@xxxxxxxxxxx Cc: jack_wang@xxxxxxxxx Cc: JBottomley@xxxxxxxxxxxxx Cc: crystal_yu@xxxxxxxxx Cc: john_gong@xxxxxxxxx Cc: lindar_liu <lindar_liu@xxxxxxxxx> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index 45bc197..3fe3d93 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -228,46 +228,140 @@ static ssize_t pm8001_ctl_logging_level_store(struct device *cdev, return -EINVAL; pm8001_ha->logging_level = val; + pm8001_update_main_config_table(pm8001_ha); return strlen(buf); } static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, pm8001_ctl_logging_level_show, pm8001_ctl_logging_level_store); + /** - * pm8001_ctl_aap_log_show - aap1 event log + * pm8001_readlog - generic routine to read a single event log entry + * @header: a pointer to the event log base header + * @entry: a pointer to copy the latest event log into + * @consumer_index: a current local index pointing to the last oldest entry + * retrieved. Automatically incremented and wrapped. + * @return: A copied entry content and a status. Status return of zero means + * more entries available, and the consumer index is incremented and + * wrapped. A positive status return means last entry, the consumer + * index may be incremented depending of approached or at the last + * entry. Status can also return a negative error code. It is the + * responsibility of the caller to confirm the validity of the copied + * event information. + */ +int pm8001_readlog( + struct eventlog_header *header, + struct eventlog_entry *entry, + u32 *consumer_index) +{ + unsigned maximum_index; + unsigned producer_index; + + /* Validity of header */ + if ((header->offset != sizeof(*header)) || + (header->entry_size != sizeof(*entry)) || + ((header->signature != EVENTLOG_HEADER_SIGNATURE_AAP1) && + (header->signature != EVENTLOG_HEADER_SIGNATURE_IOP))) + return -EINVAL; + /* Validity of hardware indexii */ + maximum_index = header->size / sizeof(*entry); + if ((maximum_index <= header->producer_index) || + (maximum_index <= header->consumer_index)) + return -EINVAL; + producer_index = header->producer_index; + if (producer_index == 0) + producer_index = maximum_index - 1; + else + --producer_index; + /* Validity of current consumer index */ + if (((producer_index < header->consumer_index) ? + ((producer_index < *consumer_index) && + (*consumer_index < header->consumer_index)) : + ((producer_index < *consumer_index) || + (*consumer_index < header->consumer_index))) || + (*consumer_index >= maximum_index)) + *consumer_index = header->consumer_index; + *entry = ((struct eventlog_entry *)(header + 1))[*consumer_index]; + if (*consumer_index != producer_index) { + ++(*consumer_index); + if (*consumer_index >= maximum_index) + *consumer_index = 0; + /* More entries? */ + return *consumer_index == producer_index; + } + return 2; /* Already at Last Entry */ +} + +/** + * pm8001_validlog - generic routine to validate a single event log entry + * @entry: a pointer to the event log entry + * @return: zero if not valid + */ +static inline int pm8001_validlog(struct eventlog_entry *entry) +{ + return ((entry->size <= (sizeof(entry->log) / sizeof(entry->log[0]))) && + ((entry->timestamp_upper != 0) || + (entry->timestamp_lower != 0))); +} + +/** + * pm8001_ctl_log_show - event log * @cdev: pointer to embedded class device * @buf: the buffer returned + * @ind: The region index (AAP1 or IOP) * * A sysfs 'read-only' shost attribute. */ -static ssize_t pm8001_ctl_aap_log_show(struct device *cdev, - struct device_attribute *attr, char *buf) +static ssize_t pm8001_ctl_log_show(struct device *cdev, char *buf, int ind) { 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; + struct eventlog_entry entry; + char *str = buf; int i; -#define AAP1_MEMMAP(r, c) \ - (*(u32 *)((u8*)pm8001_ha->memoryMap.region[AAP1].virt_ptr + (r) * 32 \ - + (c))) - char *str = buf; - int max = 2; - for (i = 0; i < max; i++) { - str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" - "0x%08x 0x%08x\n", - AAP1_MEMMAP(i, 0), - AAP1_MEMMAP(i, 4), - AAP1_MEMMAP(i, 8), - AAP1_MEMMAP(i, 12), - AAP1_MEMMAP(i, 16), - AAP1_MEMMAP(i, 20), - AAP1_MEMMAP(i, 24), - AAP1_MEMMAP(i, 28)); + memset(&entry, 0, sizeof(entry)); + do { + i = pm8001_readlog( + (struct eventlog_header *)( + pm8001_ha->memoryMap.region[ind].virt_ptr), + &entry, + (ind == AAP1) ? + &pm8001_ha->aap1_consumer : + &pm8001_ha->iop_consumer); + } while ((i == 0) && !pm8001_validlog(&entry)); + if ((i >= 0) && pm8001_validlog(&entry)) { + u32 *lp; + unsigned long long timestamp = + (((unsigned long long)entry.timestamp_upper) << 32) | + entry.timestamp_lower; + str += snprintf(str, PAGE_SIZE - (str - buf), + "%u %llu.%09llu %u", + entry.severity, + timestamp / (1000000000 / 8), + (timestamp % (1000000000 / 8)) * 8, + entry.sequence); + for (lp = entry.log, i = entry.size; i > 0; --i) + str += snprintf(str, PAGE_SIZE - (str - buf), + " 0x%08x", *(lp++)); + str += snprintf(str, PAGE_SIZE - (str - buf), "\n"); } - return str - buf; } + +/** + * pm8001_ctl_aap_log_show - aap1 event log + * @cdev: pointer to embedded class device + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_aap_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return pm8001_ctl_log_show(cdev, buf, AAP1); +} static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL); /** * pm8001_ctl_aap_log_show - IOP event log @@ -279,29 +373,7 @@ static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL); static ssize_t pm8001_ctl_iop_log_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; -#define IOP_MEMMAP(r, c) \ - (*(u32 *)((u8*)pm8001_ha->memoryMap.region[IOP].virt_ptr + (r) * 32 \ - + (c))) - int i; - char *str = buf; - int max = 2; - for (i = 0; i < max; i++) { - str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" - "0x%08x 0x%08x\n", - IOP_MEMMAP(i, 0), - IOP_MEMMAP(i, 4), - IOP_MEMMAP(i, 8), - IOP_MEMMAP(i, 12), - IOP_MEMMAP(i, 16), - IOP_MEMMAP(i, 20), - IOP_MEMMAP(i, 24), - IOP_MEMMAP(i, 28)); - } - - return str - buf; + return pm8001_ctl_log_show(cdev, buf, IOP); } static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL); diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index b7b92f7..b3bd781 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -250,8 +250,8 @@ init_default_table_values(struct pm8001_hba_info *pm8001_ha) * update_main_config_table - update the main default table to the HBA. * @pm8001_ha: our hba card information */ -static void __devinit -update_main_config_table(struct pm8001_hba_info *pm8001_ha) +void +pm8001_update_main_config_table(struct pm8001_hba_info *pm8001_ha) { void __iomem *address = pm8001_ha->main_cfg_tbl_addr; pm8001_mw32(address, 0x24, @@ -603,7 +603,7 @@ static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha) read_inbnd_queue_table(pm8001_ha); read_outbnd_queue_table(pm8001_ha); /* update main config table ,inbound table and outbound table */ - update_main_config_table(pm8001_ha); + pm8001_update_main_config_table(pm8001_ha); update_inbnd_queue_table(pm8001_ha, 0); update_outbnd_queue_table(pm8001_ha, 0); mpi_set_phys_g3_with_ssc(pm8001_ha, 0); diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index c21a216..c638da4 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -48,6 +48,8 @@ static const struct pm8001_chip_info pm8001_chips[] = { [chip_8001] = { 8, &pm8001_8001_dispatch,}, }; static int pm8001_id; +static int pm8001_logging_level = PM8001_FAIL_LOGGING; +static int pm8001_logging_size = PM8001_EVENT_LOG_SIZE; LIST_HEAD(hba_list); @@ -212,16 +214,19 @@ static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha) pm8001_ha->tags = kzalloc(PM8001_MAX_CCB, GFP_KERNEL); if (!pm8001_ha->tags) goto err_out; + pm8001_logging_size = ((pm8001_logging_size + 31) / 32) * 32; + if (pm8001_logging_size < 64) + pm8001_logging_size = 64; /* MPI Memory region 1 for AAP Event Log for fw */ pm8001_ha->memoryMap.region[AAP1].num_elements = 1; - pm8001_ha->memoryMap.region[AAP1].element_size = PM8001_EVENT_LOG_SIZE; - pm8001_ha->memoryMap.region[AAP1].total_len = PM8001_EVENT_LOG_SIZE; + pm8001_ha->memoryMap.region[AAP1].element_size = pm8001_logging_size; + pm8001_ha->memoryMap.region[AAP1].total_len = pm8001_logging_size; pm8001_ha->memoryMap.region[AAP1].alignment = 32; /* MPI Memory region 2 for IOP Event Log for fw */ pm8001_ha->memoryMap.region[IOP].num_elements = 1; - pm8001_ha->memoryMap.region[IOP].element_size = PM8001_EVENT_LOG_SIZE; - pm8001_ha->memoryMap.region[IOP].total_len = PM8001_EVENT_LOG_SIZE; + pm8001_ha->memoryMap.region[IOP].element_size = pm8001_logging_size; + pm8001_ha->memoryMap.region[IOP].total_len = pm8001_logging_size; pm8001_ha->memoryMap.region[IOP].alignment = 32; /* MPI Memory region 3 for consumer Index of inbound queues */ @@ -242,7 +247,7 @@ static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha) pm8001_ha->memoryMap.region[IB].total_len = 256 * 64; pm8001_ha->memoryMap.region[IB].alignment = 64; - /* MPI Memory region 6 inbound queues */ + /* MPI Memory region 6 outbound queues */ pm8001_ha->memoryMap.region[OB].num_elements = 256; pm8001_ha->memoryMap.region[OB].element_size = 64; pm8001_ha->memoryMap.region[OB].total_len = 256 * 64; @@ -381,7 +386,7 @@ pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost) pm8001_ha->sas = sha; pm8001_ha->shost = shost; pm8001_ha->id = pm8001_id++; - pm8001_ha->logging_level = 0x01; + pm8001_ha->logging_level = pm8001_logging_level; sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id); #ifdef PM8001_USE_TASKLET tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, @@ -614,8 +619,8 @@ intx: * @ent: pci device id * * This function is the main initialization function, when register a new - * pci driver it is invoked, all struct an hardware initilization should be done - * here, also, register interrupt + * pci driver it is invoked, all struct and hardware initialization should be + * done here, also, register interrupt */ static int __devinit pm8001_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -899,6 +904,9 @@ static void __exit pm8001_exit(void) destroy_workqueue(pm8001_wq); } +module_param(pm8001_logging_level, int, 0); +module_param_named(logging_size, pm8001_logging_size, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(logging_size, "Event logging buffer size"); module_init(pm8001_init); module_exit(pm8001_exit); diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 93959fe..84d3170 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -336,6 +336,29 @@ struct outbound_queue_table { __le32 producer_index; u32 consumer_idx; }; +struct eventlog_header { + __le32 signature; +#define EVENTLOG_HEADER_SIGNATURE_AAP1 0x1234AAAA +#define EVENTLOG_HEADER_SIGNATURE_IOP 0x5678CCCC + __le32 offset; /* sizeof(eventlog_header) */ + u32 reserved1; + __le32 size; + u32 reserved2; + __le32 consumer_index; + __le32 producer_index; + __le32 entry_size; +}; +struct eventlog_entry { + u8 size:3; + u8 reserved1:5; + u8 reserved2[2]; + u8 reserved:4; + u8 severity:4; + __le32 timestamp_upper; + __le32 timestamp_lower; + __le32 sequence; + __le32 log[4]; +}; struct pm8001_hba_memspace { void __iomem *memvirtaddr; u64 membase; @@ -383,6 +406,9 @@ struct pm8001_hba_info { u32 logging_level; u32 fw_status; const struct firmware *fw_image; + /* Local consumer indexes in support of sysfs event log node */ + u32 aap1_consumer; + u32 iop_consumer; }; struct pm8001_work { @@ -488,6 +514,11 @@ int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr, dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo, u32 mem_size, u32 align); +void pm8001_update_main_config_table(struct pm8001_hba_info *pm8001_ha); +int pm8001_readlog( + struct eventlog_header *header, + struct eventlog_entry *entry, + u32 *consumer_index); /* ctl shared API */ extern struct device_attribute *pm8001_host_attrs[]; -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html