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.
Signed-off-by: Gabriel Krisman Bertazi <kris...@linux.vnet.ibm.com> --- iprconfig.8 | 4 ++ iprconfig.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ iprlib.h | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 315 insertions(+) diff --git a/iprconfig.8 b/iprconfig.8 index ee034ca..62e7370 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 fb95bbd..56fa05e 100644 --- a/iprconfig.c +++ b/iprconfig.c @@ -18574,6 +18574,169 @@ 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_page34 page34_log; + struct ipr_sas_log_page2F page2F_log; + struct ipr_sas_log_page02 page02_log; + + struct smart_attr *attr; + char buffer[BUFSIZ]; + int rc, i, len; + uint64_t aux; + uint32_t uptime; + u8 life_remain; + uint64_t total_gb_writ; + + 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; + + for (i = 0; i < IPR_SAS_SMART_MAX_ENTRIES; i++) { + attr = &page34_log.smart_tbl[i]; + if (attr->hdr.parameter_code[1] == 0xE7) { + life_remain = attr->norm_worst_val; + } + if (attr->hdr.parameter_code[1] == 0x09) { + uptime = ntohl(*((uint32_t *) attr->raw_data)); + } + } + + rc = ipr_log_sense(dev, 0x02, &page02_log, sizeof(page02_log)); + if (rc) + return NULL; + + for(i = 0; i < IPR_SAS_LOG2_MAX_ENTRIES;) { + struct log_parameter_hdr *hdr = + (struct log_parameter_hdr *)(&(page02_log.raw_data[i])); + if (hdr->parameter_code[1] == 0x5) { + u8 *counter = &page02_log.raw_data[i+sizeof(*hdr)]; + total_gb_writ = be64toh(*(uint64_t*) counter) >> 14; + break; + } + i += hdr->length + sizeof(*hdr); + } + + rc = ipr_log_sense(dev, 0x2F, &page2F_log, sizeof(page2F_log)); + if (rc) + return NULL; + + 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"), + (page2F_log.inf_except_add_sense_code ? + "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; + } + + printf("%s", body); + + return 0; +} + static const struct { char *cmd; int min_args; @@ -18676,6 +18839,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.h b/iprlib.h index ce6c909..ce7b99f 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,83 @@ 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; +}; + +struct smart_attr { + struct log_parameter_hdr hdr; + u8 norm_threshold_val; + u8 norm_worst_val; + u8 raw_data[10]; +}; + +struct ipr_sas_log_page34 { +#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_SMART_MAX_ENTRIES 32 + struct smart_attr smart_tbl[IPR_SAS_SMART_MAX_ENTRIES]; +}; + +struct ipr_sas_log_page2F { +#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]; + + 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_page02 { +#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_LOG2_MAX_ENTRIES 128 + u8 raw_data[IPR_SAS_LOG2_MAX_ENTRIES]; +}; + struct ipr_query_ioa_caching_info { u16 len; u8 reserved[2]; @@ -2540,6 +2659,34 @@ struct ipr_ses_inquiry_pageC3 { u8 reserved2; }; +struct ipr_sas_inquiry_pageC4 { + u8 perif_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 perif_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; -- 2.1.0 ------------------------------------------------------------------------------ _______________________________________________ Iprdd-devel mailing list Iprdd-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/iprdd-devel