> 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[]; > > ______________________________________________________________________ > This email may contain privileged or confidential information, which should > only be used for the purpose for which it was sent by Xyratex. No further rights > or licenses are granted to use such information. If you are not the intended > recipient of this message, please notify the sender by return and delete it. > You may not use, copy, disclose or rely on the information contained in it. > > Internet email is susceptible to data corruption, interception and > unauthorised amendment for which Xyratex does not accept liability. While we > have taken reasonable precautions to ensure that this email is free of viruses, > Xyratex does not accept liability for the presence of any computer viruses in > this email, nor for any losses caused as a result of viruses. > > Xyratex Technology Limited (03134912), Registered in England & Wales, > Registered Office, Langstone Road, Havant, Hampshire, PO9 1SA. > > The Xyratex group of companies also includes, Xyratex Ltd, registered in > Bermuda, Xyratex International Inc, registered in California, Xyratex > (Malaysia) Sdn Bhd registered in Malaysia, Xyratex Technology (Wuxi) Co Ltd > registered in The People's Republic of China and Xyratex Japan Limited > registered in Japan. > ______________________________________________________________________ > -- 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