Brian King <brk...@linux.vnet.ibm.com> writes: > Overall, looks good. Just a few minor style nits.
Hi Brian, thanks for your review. Here is a follow-up patch fixing the things you pointed out. Thanks, -- >8 -- From: Gabriel Krisman Bertazi <kris...@linux.vnet.ibm.com> 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 v2: - Change uint32_t to u32. Likewise for uint64_t. - Change perif_type to peri_qual_dev_type in log page C4h and C7h. - Change error code returned in ssd_report to -EIO. 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 | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ iprlib.c | 45 +++++++++++++++++ iprlib.h | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 343 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 65db394..a57f350 100644 --- a/iprconfig.c +++ b/iprconfig.c @@ -18711,6 +18711,168 @@ 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; + u64 aux; + u64 total_gb_writ = 0; + u32 uptime = 0; + u32 life_remain = 0; + 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(*((u32 *) 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(*(u64 *) &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(*((u32 *) 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 -EIO; + } + + printf("%s", body); + + return 0; +} + static const struct { char *cmd; int min_args; @@ -18814,6 +18976,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..4c7229a 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, + u32 param_code, int *entries_cntr) +{ + int page_length; + int i; + const u8 *raw_data = page->raw_data; + u32 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..6aad99d 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 peri_qual_dev_type; + 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 peri_qual_dev_type; + 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, u32 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 *); -- 2.1.0 ------------------------------------------------------------------------------ Go from Idea to Many App Stores Faster with Intel(R) XDK Give your users amazing mobile app experiences with Intel(R) XDK. Use one codebase in this all-in-one HTML5 development environment. Design, debug & build mobile apps & 2D/3D high-impact games for multiple OSs. http://pubads.g.doubleclick.net/gampad/clk?id=254741551&iu=/4140 _______________________________________________ Iprdd-devel mailing list Iprdd-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/iprdd-devel