Overall, looks good. Just a few minor style nits. Thanks,
Brian On 11/18/2015 10:05 AM, Gabriel Krisman Bertazi wrote: > Sorry, I screwed up when sending the lastest version of this patch. > Forgot to add the v2. Resending... > > -- >8 -- > > New ssd-report command allow users to inquiry some info on their > read-intensive SSDs, like life expectancy and usage stats, so they can > know when their SSD will wear out. > > Changes since v1: > - Comply to the latest SAS Spec. > - Fix negative life remaining values. > - Trigger PFA Trip if other parameters exist in the log page. > - Implement common interface in iprlib to search specific parameters in > a log page. > > Signed-off-by: Gabriel Krisman Bertazi <kris...@linux.vnet.ibm.com> > --- > iprconfig.8 | 4 ++ > iprconfig.c | 165 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > iprlib.c | 45 +++++++++++++++++ > iprlib.h | 131 +++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 345 insertions(+) > > diff --git a/iprconfig.8 b/iprconfig.8 > index 9f25902..6c8c3dd 100644 > --- a/iprconfig.8 > +++ b/iprconfig.8 > @@ -337,6 +337,10 @@ Show the microcode level that is currently loaded on the > specified device. > Note: The device specified may be the sg device associated with an IOA, > in which case the IOA's microcode level will be shown. > .TP > +.B ssd-report [device] > +.br > +Display information about Read Intensive SSD devices in the system. > +.TP > .B show-ucode-levels > .br > Show the microcode level that is currently loaded for every device and > diff --git a/iprconfig.c b/iprconfig.c > index c4499a6..af055da 100644 > --- a/iprconfig.c > +++ b/iprconfig.c > @@ -18708,6 +18708,170 @@ static int dump (char **args, int num_args) > return 0; > } > > +static char *print_ssd_report(struct ipr_dev *dev, char *body) > +{ > + struct ipr_inquiry_page0 page0_inq; > + struct ipr_dasd_inquiry_page3 page3_inq; > + struct ipr_sas_std_inq_data std_inq; > + struct ipr_sas_inquiry_pageC4 pageC4_inq; > + struct ipr_sas_inquiry_pageC7 pageC7_inq; > + struct ipr_sas_log_page page34_log; > + struct ipr_sas_log_page page2F_log; > + struct ipr_sas_log_page page02_log; > + > + struct ipr_sas_log_smart_attr *smart_attr; > + struct ipr_sas_log_inf_except_attr *inf_except_attr; > + struct ipr_sas_log_write_err_cnt_attr *bytes_counter; > + > + char buffer[BUFSIZ]; > + int rc, len, nentries = 0; > + uint64_t aux; > + uint32_t uptime = 0; > + uint32_t life_remain = 0; > + uint64_t total_gb_writ = 0; Can we change these to use u32 / u64 types to be consistent? > + int pfa_trip = 0; > + > + memset(&std_inq, 0, sizeof(std_inq)); > + memset(&pageC4_inq, 0, sizeof(pageC4_inq)); > + memset(&pageC7_inq, 0, sizeof(pageC7_inq)); > + memset(&page0_inq, 0, sizeof(page0_inq)); > + memset(&page3_inq, 0, sizeof(page3_inq)); > + memset(&page34_log, 0, sizeof(page34_log)); > + memset(&page2F_log, 0, sizeof(page2F_log)); > + memset(&page02_log, 0, sizeof(page02_log)); > + > + rc = ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq, sizeof(std_inq)); > + if (rc) > + return NULL; > + > + if (!std_inq.is_ssd) { > + scsi_err(dev, "%s is not a SSD.\n", dev->gen_name); > + return NULL; > + } > + > + rc = ipr_inquiry(dev, 0xC4, &pageC4_inq, sizeof(pageC4_inq)); > + if (rc) > + return NULL; > + > + if (pageC4_inq.endurance != IPR_SAS_ENDURANCE_LOW_EZ && > + pageC4_inq.endurance != IPR_SAS_ENDURANCE_LOW3 && > + pageC4_inq.endurance != IPR_SAS_ENDURANCE_LOW1) { > + scsi_err(dev, "%s is not a Read Intensive SSD.\n", > + dev->gen_name); > + return NULL; > + } > + > + rc = ipr_inquiry(dev, 0x3, &page3_inq, sizeof(page3_inq)); > + if (rc) > + return NULL; > + > + rc = ipr_inquiry(dev, 0xC7, &pageC7_inq, sizeof(pageC7_inq)); > + if (rc) > + return NULL; > + > + rc = ipr_log_sense(dev, 0x34, &page34_log, sizeof(page34_log)); > + if (rc) > + return NULL; > + > + smart_attr = ipr_sas_log_get_param(&page34_log, 0xE7, NULL); > + if (smart_attr && smart_attr->norm_worst_val > 0) > + life_remain = smart_attr->norm_worst_val; > + > + smart_attr = ipr_sas_log_get_param(&page34_log, 0x09, NULL); > + if (smart_attr) > + uptime = ntohl(*((uint32_t *) smart_attr->raw_data)); > + > + rc = ipr_log_sense(dev, 0x02, &page02_log, sizeof(page02_log)); > + if (rc) > + return NULL; > + > + bytes_counter = ipr_sas_log_get_param(&page02_log, 0x05, NULL); > + if (bytes_counter) > + total_gb_writ = be64toh(*(uint64_t*) > + &bytes_counter->counter) >> 14; > + > + > + rc = ipr_log_sense(dev, 0x2F, &page2F_log, sizeof(page2F_log)); > + if (rc) > + return NULL; > + > + inf_except_attr = ipr_sas_log_get_param(&page2F_log, 0x0, &nentries); > + if (inf_except_attr) > + pfa_trip = inf_except_attr->inf_except_add_sense_code; > + if (nentries > 1) > + pfa_trip = 1; > + > + body = add_line_to_body(body, "", NULL); > + /* FRU Number */ > + ipr_strncpy_0(buffer, std_inq.asm_part_num, > + IPR_SAS_STD_INQ_ASM_PART_NUM); > + body = add_line_to_body(body, _("FRU Number"), buffer); > + > + /* Serial number */ > + ipr_strncpy_0(buffer, (char *)std_inq.std_inq_data.serial_num, > + IPR_SERIAL_NUM_LEN); > + body = add_line_to_body(body, _("Serial Number"), buffer); > + > + /* FW level */ > + len = sprintf(buffer, "%X%X%X%X", page3_inq.release_level[0], > + page3_inq.release_level[1], page3_inq.release_level[2], > + page3_inq.release_level[3]); > + > + if (isalnum(page3_inq.release_level[0]) && > + isalnum(page3_inq.release_level[1]) && > + isalnum(page3_inq.release_level[2]) && > + isalnum(page3_inq.release_level[3])) > + sprintf(buffer + len, " (%c%c%c%c)", page3_inq.release_level[0], > + page3_inq.release_level[1], page3_inq.release_level[2], > + page3_inq.release_level[3]); > + > + body = add_line_to_body(body, _("Firmware Version"), buffer); > + > + /* Bytes written */ > + snprintf(buffer, BUFSIZ, "%ld GB", total_gb_writ); > + body = add_line_to_body(body, _("Total Bytes Written"), buffer); > + > + /* Max bytes. */ > + aux = ntohl(*((uint32_t *) pageC7_inq.total_bytes_warranty)) >> 8; > + snprintf(buffer, BUFSIZ, "%ld GB", aux * 1024L); > + body = add_line_to_body(body, _("Number of Bytes reported by Warranty"), > + buffer); > + > + /* Life remaining */ > + snprintf(buffer, BUFSIZ, "%hhd %%", life_remain); > + body = add_line_to_body(body, _("Life Remaining Gauge"), buffer); > + > + /* PFA Trip */ > + body = add_line_to_body(body, _("PFA Trip"), pfa_trip? "yes": "no"); > + > + /* Power-on days */ > + snprintf (buffer, 4, "%d", uptime / 24); > + body = add_line_to_body(body, _("Power-on Days"), buffer); > + > + return body; > +} > + > +static int ssd_report(char **args, int num_args) > +{ > + struct ipr_dev *dev = find_dev(args[0]); > + char *body; > + > + if (!dev) { > + fprintf(stderr, "Cannot find %s\n", args[0]); > + return -EINVAL; > + } > + > + body = print_ssd_report(dev, NULL); > + if (!body) { > + scsi_err(dev, "Device inquiry failed.\n"); > + return 1; Would -EIO be better here perhaps? > + } > + > + printf("%s", body); > + > + return 0; > +} > + > static const struct { > char *cmd; > int min_args; > @@ -18811,6 +18975,7 @@ static const struct { > { "set-log-level", 2, 0, 2, set_log_level_cmd, > "sg5 2" }, > { "set-write-cache-policy", 2, 0, 2, > set_device_write_cache_policy, "sg5 [writeback|writethrough]" }, > { "query-write-cache-policy", 1, 0, 1, > query_device_write_cache_policy, "sg5" }, > + { "ssd-report", 1, 0, 1, ssd_report, "sg5" }, > { "identify-disk", 2, 0, 2, identify_disk, "sda 1" > }, > { "identify-slot", 2, 0, 2, identify_slot, > "U5886.001.P915059-P1-D1 1" }, > { "remove-disk", 2, 0, 2, remove_disk, "sda 1" }, > diff --git a/iprlib.c b/iprlib.c > index 6e5a487..b0ff857 100644 > --- a/iprlib.c > +++ b/iprlib.c > @@ -3038,6 +3038,51 @@ int ipr_is_log_page_supported(struct ipr_dev *dev, u8 > page) > return 0; > } > > +/** ipr_sas_log_get_param - Fetch parameter from log page. > + * > + * Iterate over an already fetched log page pointed by page and return a > + * pointer to the beginning of the parameter. > + * > + * @page: An already fetched log page. > + * @param: to be fetched > + * > + * Returns: @dst: A pointer to the original page area starting at the > + * parameter. NULL if parameter was not found. > + * @entries_cnt: output parameter, the number of entries found in the > + * page. > + **/ > +void *ipr_sas_log_get_param(const struct ipr_sas_log_page *page, > + uint32_t param_code, int *entries_cntr) Can you change this uint32_t to a u32 to be consistent with the rest of the code? > +{ > + int page_length; > + int i; > + const u8 *raw_data = page->raw_data; > + uint32_t cur_code; > + struct log_parameter_hdr *hdr; > + void *ret = NULL; > + > + if (entries_cntr) > + *entries_cntr = 0; > + > + page_length = (page->page_length[0] << 8) | page->page_length[1]; > + > + for(i = 0; i < page_length && i < IPR_SAS_LOG_MAX_ENTRIES; > + i += hdr->length + sizeof(*hdr)) { > + hdr = (struct log_parameter_hdr *) &raw_data[i]; > + cur_code = (hdr->parameter_code[0] << 8) | > hdr->parameter_code[1]; > + > + if (cur_code == param_code) { > + ret = hdr; > + if (!entries_cntr) > + break; > + } > + > + if (entries_cntr) > + *entries_cntr += 1; > + } > + return ret; > +} > + > /** > * ipr_get_blk_size - return the block size for the given device > * @dev: ipr dev struct > diff --git a/iprlib.h b/iprlib.h > index cf6bf79..8d6c363 100644 > --- a/iprlib.h > +++ b/iprlib.h > @@ -236,6 +236,18 @@ typedef uint64_t u64; > #define IPR_VSET_VIRTUAL_BUS 0x2 > #define IPR_IOAFP_VIRTUAL_BUS 0x3 > > +#define IPR_SAS_STD_INQ_UCODE_ID 12 > +#define IPR_SAS_STD_INQ_VENDOR_UNIQ 40 > +#define IPR_SAS_STD_INQ_PLANT_MAN 4 > +#define IPR_SAS_STD_INQ_DATE_MAN 5 > +#define IPR_SAS_STD_INQ_FRU_COUNT 4 > +#define IPR_SAS_STD_INQ_FRU_FIELD_LEN 2 > +#define IPR_SAS_STD_INQ_FRU_PN 12 > +#define IPR_SAS_STD_INQ_ASM_EC_LVL 10 > +#define IPR_SAS_STD_INQ_ASM_PART_NUM 12 > +#define IPR_SAS_STD_INQ_FRU_ASM_EC 10 > +#define IPR_SAS_INQ_BYTES_WARRANTY_LEN 3 > + > /* Device write cache policies. */ > enum {IPR_DEV_CACHE_WRITE_THROUGH = 0, IPR_DEV_CACHE_WRITE_BACK}; > > @@ -1202,6 +1214,36 @@ struct ipr_std_inq_data_long { > u8 z6_term[IPR_STD_INQ_Z6_TERM_LEN]; > }; > > +struct ipr_sas_std_inq_data { > + struct ipr_std_inq_data std_inq_data; > + u8 microcode_id[IPR_SAS_STD_INQ_UCODE_ID]; > + u8 reserved1; > + u8 vendor_unique[IPR_SAS_STD_INQ_VENDOR_UNIQ]; > + > +#if defined (__BIG_ENDIAN_BITFIELD) > + u8 reserved2:5; > + u8 is_ssd:1; > + u8 near_line:1; > + u8 unlock:1; > +#elif defined (__LITTLE_ENDIAN_BITFIELD) > + u8 unlock:1; > + u8 near_line:1; > + u8 is_ssd:1; > + u8 reserved2:5; > +#endif > + > + u8 plant_manufacture[IPR_SAS_STD_INQ_PLANT_MAN]; > + u8 date_manufacture[IPR_SAS_STD_INQ_DATE_MAN]; > + u8 vendor_unique_pn; > + u8 fru_count[IPR_SAS_STD_INQ_FRU_COUNT]; > + u8 fru_field_len[IPR_SAS_STD_INQ_FRU_FIELD_LEN]; > + u8 fru_pn[IPR_SAS_STD_INQ_FRU_PN]; > + u8 asm_ec_level[IPR_SAS_STD_INQ_ASM_EC_LVL]; > + u8 asm_part_num[IPR_SAS_STD_INQ_ASM_PART_NUM]; > + u8 fru_asm_ec[IPR_SAS_STD_INQ_FRU_ASM_EC]; > + u8 reserved3[6]; > +}; > + > struct ipr_mode_page_28_scsi_dev_bus_attr { > struct ipr_res_addr res_addr; > > @@ -2216,6 +2258,65 @@ struct ipr_global_cache_params_term { > u8 reserved3; > }; > > +struct log_parameter_hdr { > + u8 parameter_code[2]; > + > +#if defined (__BIG_ENDIAN_BITFIELD) > + u8 du:1; > + u8 reserved1:1; > + u8 tsd:1; > + u8 etc:1; > + u8 tmc:2; > + u8 lbin:1; > + u8 lp:1; > +#elif defined (__LITTLE_ENDIAN_BITFIELD) > + u8 lp:1; > + u8 lbin:1; > + u8 tmc:2; > + u8 etc:1; > + u8 tsd:1; > + u8 reserved1:1; > + u8 du:1; > +#endif > + u8 length; > +}; > + > +/* Log Parameter: Log Page = 0x34. Attribute template */ > +struct ipr_sas_log_smart_attr { > + struct log_parameter_hdr hdr; > + u8 norm_threshold_val; > + int8_t norm_worst_val; > + u8 raw_data[10]; > +}; > + > +/* Log Parameter: Log Page = 0x2F. Attribute Code = 0x0 */ > +struct ipr_sas_log_inf_except_attr { > + struct log_parameter_hdr param_hdr; > + u8 inf_except_add_sense_code; > + u8 inf_except_add_sense_code_qual; > + u8 last_temp_read; > +}; > + > +struct ipr_sas_log_write_err_cnt_attr { > + struct log_parameter_hdr param_hdr; > + u8 counter; > +}; > + > +struct ipr_sas_log_page { > +#if defined (__BIG_ENDIAN_BITFIELD) > + u8 reserved1:2; > + u8 page_code:6; > +#elif defined (__LITTLE_ENDIAN_BITFIELD) > + u8 page_code:6; > + u8 reserved1:2; > +#endif > + u8 reserved2; > + u8 page_length[2]; > + > +#define IPR_SAS_LOG_MAX_ENTRIES 256 > + u8 raw_data[IPR_SAS_LOG_MAX_ENTRIES]; > +}; > + > struct ipr_query_ioa_caching_info { > u16 len; > u8 reserved[2]; > @@ -2540,6 +2641,34 @@ struct ipr_ses_inquiry_pageC3 { > u8 reserved2; > }; > > +struct ipr_sas_inquiry_pageC4 { > + u8 perif_type; Can you rename this to peri_qual_dev_type to be similar to the other pages? > + u8 page_code; > + u8 reserved1; > + u8 page_length; > +#define IPR_SAS_ENDURANCE_HIGH_HDD 0x20 > +#define IPR_SAS_ENDURANCE_LOW_EZ 0x31 > +#define IPR_SAS_ENDURANCE_LOW3 0x32 > +#define IPR_SAS_ENDURANCE_LOW1 0x33 > + u8 endurance; > + u8 reserved[6]; > + u8 revision; > + u8 serial_num_11s[8]; > + u8 serial_num_supplier[8]; > + u8 master_drive_part[12]; > +}; > + > +struct ipr_sas_inquiry_pageC7 { > + u8 perif_type; Can you rename this to peri_qual_dev_type to be similar to the other pages? > + u8 page_code; > + u8 reserved1; > + u8 page_length; > + u8 ascii_len; > + u8 reserved2[109]; > + u8 total_bytes_warranty[IPR_SAS_INQ_BYTES_WARRANTY_LEN]; > + u8 reserved3[43]; > +}; > + > static inline int ipr_elem_offset(struct ipr_ses_config_pg *ses_cfg, u8 type) > { > int i, off; > @@ -2646,6 +2775,8 @@ int ipr_mode_sense(struct ipr_dev *, u8, void *); > int ipr_mode_select(struct ipr_dev *, void *, int); > int ipr_log_sense(struct ipr_dev *, u8, void *, u16); > int ipr_is_log_page_supported(struct ipr_dev *, u8); > +void *ipr_sas_log_get_param(const struct ipr_sas_log_page *page, uint32_t > param, > + int *entries_cnt); > int ipr_reset_device(struct ipr_dev *); > int ipr_re_read_partition(struct ipr_dev *); > int ipr_read_capacity(struct ipr_dev *, void *); > -- Brian King Power Linux I/O IBM Linux Technology Center ------------------------------------------------------------------------------ _______________________________________________ Iprdd-devel mailing list Iprdd-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/iprdd-devel