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

Reply via email to