>The user that needed this was PMC engineering, you see they needed the event log, along with their proprietary <sigh> forensics, to debug a failure on the SAS bus and report that back to us. The test was their satisfaction when they provided a response to the trouble request with an accurate portrayal of the root cause. Yes, PMC always ask for log to analyze failure in the chip. > >My unit test (more than a year ago) was to run a system for a day with a high event level, fiddling cables, then pull the events, watching for the boundary condition (stutter of the last entry). Content was otherwise opaque to me. Thanks for share, this is very helpful. > >ToDo is to add a 'write' to the node to reset the outgoing pointer and to add a string describing the events as part of the numerical data; but there is no impetuous for that right now. > Yes, I think this is a low priority task. Thanks Jack >Sincerely -- Mark Salyzyn > >On Jan 19, 2012, at 2:37 AM, Jack Wang wrote: > >>> 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. >>> >> [Jack Wang] Patch is welcome. >>> 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. >>> >> [Jack Wang] I wonder what user need this function? How do you test this >> function? >> >>> 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> >> >> [Jack Wang] >> Acked-by: Jack Wang <jack_wang@xxxxxxxxx> >> Thanks. >> >>> >>> 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 > >__________ Information from ESET NOD32 Antivirus, version of virus signature database 5659 (20101129) __________ > >The message was checked by ESET NOD32 Antivirus. > >http://www.eset.com > > >?韬{.n?????%??檩??w?{.n???{炳??Ф?塄}?财??j:+v??????2??璀??摺?囤??z夸z罐?+?????w棹f