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;
+       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;
+       }
+
+       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)
+{
+       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;
+       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;
@@ -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 *);
-- 
2.1.0


------------------------------------------------------------------------------
_______________________________________________
Iprdd-devel mailing list
Iprdd-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/iprdd-devel

Reply via email to