From 82b24b82aff7efe4aaf6c4b8d6f4382d6c3b902f Mon Sep 17 00:00:00 2001 From: Randy Date: Mon, 18 May 2020 10:31:57 -0500 Subject: [PATCH] Add log page CA parsing Add parsing version for 0xCA Changes from review. Change customer ids --- plugins/wdc/wdc-nvme.c | 433 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 411 insertions(+), 22 deletions(-) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 280f9fa..a7329a6 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -121,6 +121,12 @@ #define SN730_GET_EXTEND_LOG_SUBOPCODE 0x00040009 #define SN730_LOG_CHUNK_SIZE 0x1000 +/* Customer ID's */ +#define WDC_CUSTOMER_ID_GENERIC 0x0001 +#define WDC_CUSTOMER_ID_0x1002 0x1002 +#define WDC_CUSTOMER_ID_0x1004 0x1004 +#define WDC_CUSTOMER_ID_0x1005 0x1005 + /* Drive Resize */ #define WDC_NVME_DRIVE_RESIZE_OPCODE 0xCC #define WDC_NVME_DRIVE_RESIZE_CMD 0x03 @@ -227,6 +233,7 @@ #define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE 0xC2 #define WDC_C2_LOG_BUF_LEN 0x1000 #define WDC_C2_LOG_PAGES_SUPPORTED_ID 0x08 +#define WDC_C2_CUSTOMER_ID_ID 0x15 #define WDC_C2_THERMAL_THROTTLE_STATUS_ID 0x18 #define WDC_C2_ASSERT_DUMP_PRESENT_ID 0x19 #define WDC_C2_USER_EOL_STATUS_ID 0x1A @@ -247,11 +254,12 @@ /* CA Log Page */ #define WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE 0xCA -#define WDC_CA_LOG_BUF_LEN 0x80 +#define WDC_FB_CA_LOG_BUF_LEN 0x80 +#define WDC_BD_CA_LOG_BUF_LEN 0x9C /* C0 EOL Status Log Page */ #define WDC_NVME_GET_EOL_STATUS_LOG_OPCODE 0xC0 -#define WDC_NVME_EOL_STATUS_LOG_LEN 0x200 +#define WDC_NVME_EOL_STATUS_LOG_LEN 0x200 /* CB - FW Activate History Log Page */ #define WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID 0xCB @@ -259,7 +267,7 @@ /* D0 Smart Log Page */ #define WDC_NVME_GET_VU_SMART_LOG_OPCODE 0xD0 -#define WDC_NVME_VU_SMART_LOG_LEN 0x200 +#define WDC_NVME_VU_SMART_LOG_LEN 0x200 /* Log Page Directory defines */ #define NVME_LOG_PERSISTENT_EVENT 0x0D @@ -626,6 +634,14 @@ struct wdc_c2_cbs_data { __u8 data[]; }; +struct wdc_bd_ca_log_format { + __u8 field_id; + __u8 reserved1[2]; + __u8 normalized_value; + __u8 reserved2; + __u8 raw_value[7]; +}; + struct __attribute__((__packed__)) wdc_ssd_ca_perf_stats { __le64 nand_bytes_wr_lo; /* 0x00 - NAND Bytes Written lo */ __le64 nand_bytes_wr_hi; /* 0x08 - NAND Bytes Written hi */ @@ -2773,7 +2789,7 @@ static int wdc_print_log(struct wdc_ssd_perf_stats *perf, int fmt) return 0; } -static void wdc_print_ca_log_normal(struct wdc_ssd_ca_perf_stats *perf) +static void wdc_print_fb_ca_log_normal(struct wdc_ssd_ca_perf_stats *perf) { uint64_t converted = 0; @@ -2836,7 +2852,7 @@ static void wdc_print_ca_log_normal(struct wdc_ssd_ca_perf_stats *perf) perf->percent_free_blocks); } -static void wdc_print_ca_log_json(struct wdc_ssd_ca_perf_stats *perf) +static void wdc_print_fb_ca_log_json(struct wdc_ssd_ca_perf_stats *perf) { struct json_object *root; uint64_t converted = 0; @@ -2897,6 +2913,277 @@ static void wdc_print_ca_log_json(struct wdc_ssd_ca_perf_stats *perf) json_free_object(root); } +static void wdc_print_bd_ca_log_normal(void *data) +{ + struct wdc_bd_ca_log_format *bd_data = (struct wdc_bd_ca_log_format *)data; + __u64 *raw; + __u16 *word_raw; + __u32 *dword_raw; + __u8 *byte_raw; + + if (bd_data->field_id == 0x00) { + raw = (__u64*)bd_data->raw_value; + printf(" CA Log Page values :- \n"); + printf(" Program fail counts %20"PRIu64"\n", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf(" %% Remaining of allowable program fails %3"PRIu8"\n", + bd_data->normalized_value); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x01) { + raw = (__u64*)bd_data->raw_value; + printf(" Erase fail count %20"PRIu64"\n", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf(" %% Remaining of allowable erase fails %3"PRIu8"\n", + bd_data->normalized_value); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x02) { + word_raw = (__u16*)bd_data->raw_value; + printf(" Min erase cycles %10"PRIu16"\n", + le16_to_cpu(*word_raw)); + word_raw = (__u16*)&bd_data->raw_value[2]; + printf(" Max erase cycles %10"PRIu16"\n", + le16_to_cpu(*word_raw)); + word_raw = (__u16*)&bd_data->raw_value[4]; + printf(" Ave erase cycles %10"PRIu16"\n", + le16_to_cpu(*word_raw)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x03) { + raw = (__u64*)bd_data->raw_value; + printf(" End to end error detection count %20"PRIu64"\n", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x04) { + raw = (__u64*)bd_data->raw_value; + printf(" Crc error count %20"PRIu64"\n", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x05) { + raw = (__u64*)bd_data->raw_value; + printf(" Timed workload media error %20.3f\n", + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x06) { + raw = (__u64*)bd_data->raw_value; + printf(" Timed workload host reads %% %3"PRIu64"\n", + le64_to_cpu(*raw & 0x00000000000000FF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x07) { + raw = (__u64*)bd_data->raw_value; + printf(" Timed workload timer %20"PRIu64"\n", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x08) { + byte_raw = (__u8*)bd_data->raw_value; + printf(" Throttle status %% %10"PRIu16"\n", + *byte_raw); + dword_raw = (__u32*)&bd_data->raw_value[1]; + printf(" Throttling event counter %10"PRIu16"\n", + le32_to_cpu(*dword_raw)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x09) { + raw = (__u64*)bd_data->raw_value; + printf(" Retry buffer overflow count %20"PRIu64"\n", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x0A) { + raw = (__u64*)bd_data->raw_value; + printf(" Pll lock loss count %20"PRIu64"\n", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x0B) { + raw = (__u64*)bd_data->raw_value; + printf(" Nand bytes written (32mb) %20.0f\n", + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + raw = (__u64*)bd_data->raw_value; + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x0C) { + raw = (__u64*)bd_data->raw_value; + printf(" Host bytes written (32mb) %20.0f\n", + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + raw = (__u64*)bd_data->raw_value; + } else { + goto invalid_id; + } + + goto done; + + invalid_id: + printf(" Invalid Field ID = %d\n", bd_data->field_id); + + done: + return; + +} + +static void wdc_print_bd_ca_log_json(void *data) +{ + struct wdc_bd_ca_log_format *bd_data = (struct wdc_bd_ca_log_format *)data; + __u64 *raw; + __u16 *word_raw; + __u32 *dword_raw; + __u8 *byte_raw; + struct json_object *root; + + root = json_create_object(); + if (bd_data->field_id == 0x00) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "Program fail counts", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + json_object_add_value_int(root, "% Remaining of allowable program fails", + bd_data->normalized_value); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x01) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "Erase fail count", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + json_object_add_value_int(root, "% Remaining of allowable erase fails", + bd_data->normalized_value); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x02) { + word_raw = (__u16*)bd_data->raw_value; + json_object_add_value_int(root, "Min erase cycles", le16_to_cpu(*word_raw)); + word_raw = (__u16*)&bd_data->raw_value[2]; + json_object_add_value_int(root, "Max erase cycles", le16_to_cpu(*word_raw)); + word_raw = (__u16*)&bd_data->raw_value[4]; + json_object_add_value_int(root, "Ave erase cycles", le16_to_cpu(*word_raw)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x03) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "End to end error detection count", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x04) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "Crc error count", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x05) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_float(root, "Timed workload media error", + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x06) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "Timed workload host reads %", + le64_to_cpu(*raw & 0x00000000000000FF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x07) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "Timed workload timer", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x08) { + byte_raw = (__u8*)bd_data->raw_value; + json_object_add_value_int(root, "Throttle status %", *byte_raw); + dword_raw = (__u32*)&bd_data->raw_value[1]; + json_object_add_value_int(root, "Throttling event counter", le32_to_cpu(*dword_raw)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x09) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "Retry buffer overflow count", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x0A) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_int(root, "Pll lock loss count", + le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x0B) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_float(root, "Nand bytes written (32mb)", + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + } else { + goto invalid_id; + } + bd_data++; + if (bd_data->field_id == 0x0C) { + raw = (__u64*)bd_data->raw_value; + json_object_add_value_float(root, "Host bytes written (32mb)", + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + raw = (__u64*)bd_data->raw_value; + } else { + goto invalid_id; + } + + goto done; + + invalid_id: + printf(" Invalid Field ID = %d\n", bd_data->field_id); + + done: + return; + +} + static void wdc_print_d0_log_normal(struct wdc_ssd_d0_smart_log *perf) { printf(" D0 Smart Log Page Statistics :- \n"); @@ -3151,7 +3438,7 @@ static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entr json_free_object(root); } -static int wdc_print_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt) +static int wdc_print_fb_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt) { if (!perf) { fprintf(stderr, "ERROR : WDC : Invalid buffer to read perf stats\n"); @@ -3159,10 +3446,27 @@ static int wdc_print_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt) } switch (fmt) { case NORMAL: - wdc_print_ca_log_normal(perf); + wdc_print_fb_ca_log_normal(perf); break; case JSON: - wdc_print_ca_log_json(perf); + wdc_print_fb_ca_log_json(perf); + break; + } + return 0; +} + +static int wdc_print_bd_ca_log(void *bd_data, int fmt) +{ + if (!bd_data) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read data\n"); + return -1; + } + switch (fmt) { + case NORMAL: + wdc_print_bd_ca_log_normal(bd_data); + break; + case JSON: + wdc_print_bd_ca_log_json(bd_data); break; } return 0; @@ -3210,7 +3514,9 @@ static int wdc_get_ca_log_page(int fd, char *format) int ret = 0; int fmt = -1; __u8 *data; + __u32 *cust_id; struct wdc_ssd_ca_perf_stats *perf; + uint32_t read_device_id, read_vendor_id; if (!wdc_check_device(fd)) return -1; @@ -3226,24 +3532,107 @@ static int wdc_get_ca_log_page(int fd, char *format) return -1; } - if ((data = (__u8*) malloc(sizeof (__u8) * WDC_CA_LOG_BUF_LEN)) == NULL) { - fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); return -1; } - memset(data, 0, sizeof (__u8) * WDC_CA_LOG_BUF_LEN); - ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, - false, WDC_CA_LOG_BUF_LEN, data); - if (strcmp(format, "json")) - fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id); - if (ret == 0) { - /* parse the data */ - perf = (struct wdc_ssd_ca_perf_stats *)(data); - ret = wdc_print_ca_log(perf, fmt); - } else { - fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n"); - ret = -1; + cust_id = (__u32*)data; + + switch (read_device_id) { + + case WDC_NVME_SN200_DEV_ID: + + if (*cust_id == WDC_CUSTOMER_ID_0x1005) { + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN); + + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, + false, WDC_FB_CA_LOG_BUF_LEN, data); + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + perf = (struct wdc_ssd_ca_perf_stats *)(data); + ret = wdc_print_fb_ca_log(perf, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n"); + ret = -1; + } + } else { + + fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id); + } + break; + + case WDC_NVME_SN640_DEV_ID: + case WDC_NVME_SN640_DEV_ID_1: + case WDC_NVME_SN640_DEV_ID_2: + case WDC_NVME_SN840_DEV_ID: + case WDC_NVME_SN840_DEV_ID_1: + + if (*cust_id == WDC_CUSTOMER_ID_0x1005) { + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN); + + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, + false, WDC_FB_CA_LOG_BUF_LEN, data); + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + perf = (struct wdc_ssd_ca_perf_stats *)(data); + ret = wdc_print_fb_ca_log(perf, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n"); + ret = -1; + } + } else if (*cust_id == WDC_CUSTOMER_ID_GENERIC) { + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + memset(data, 0, sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN); + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, + false, WDC_FB_CA_LOG_BUF_LEN, data); + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + ret = wdc_print_bd_ca_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n"); + ret = -1; + } + + break; + } else { + + fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id); + } + break; + + default: + + fprintf(stderr, "ERROR : WDC : Log page 0xCA not supported for this device\n"); + break; } free(data); -- 2.49.0