summaryrefslogtreecommitdiffstats
path: root/plugins/wdc
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/wdc')
-rw-r--r--plugins/wdc/wdc-nvme.c445
-rw-r--r--plugins/wdc/wdc-nvme.h3
2 files changed, 432 insertions, 16 deletions
diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c
index 4468344..d5e17b8 100644
--- a/plugins/wdc/wdc-nvme.c
+++ b/plugins/wdc/wdc-nvme.c
@@ -79,6 +79,7 @@
#define WDC_NVME_SN650_DEV_ID_1 0x2701
#define WDC_NVME_SN650_DEV_ID_2 0x2702
#define WDC_NVME_SN650_DEV_ID_3 0x2720
+#define WDC_NVME_SN650_DEV_ID_4 0x2721
#define WDC_NVME_SN450_DEV_ID_1 0x2712
#define WDC_NVME_SN450_DEV_ID_2 0x2713
#define WDC_NVME_SXSLCL_DEV_ID 0x2001
@@ -122,6 +123,7 @@
#define WDC_DRIVE_CAP_CLOUD_SSD_VERSION 0x0000000004000000
#define WDC_DRIVE_CAP_PCIE_STATS 0x0000000008000000
#define WDC_DRIVE_CAP_INFO_2 0x0000000010000000
+#define WDC_DRIVE_CAP_C3_LOG_PAGE 0x0000000020000000
#define WDC_DRIVE_CAP_DRIVE_ESSENTIALS 0x0000000100000000
#define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000
@@ -322,6 +324,15 @@
#define WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN 0x1000
#define WDC_MAX_NUM_ACT_HIST_ENTRIES 20
+/* C3 Latency Monitor Log Page */
+#define WDC_LATENCY_MON_LOG_BUF_LEN 0x200
+#define WDC_LATENCY_MON_OPCODE 0xC3
+#define WDC_LATENCY_MON_VERSION 0x0001
+
+#define WDC_C3_GUID_LENGTH 16
+static __u8 wdc_lat_mon_guid[WDC_C3_GUID_LENGTH] = { 0x92, 0x7a, 0xc0, 0x8c, 0xd0, 0x84, 0x6c, 0x9c,
+ 0x70, 0x43, 0xe6, 0xd4, 0x58, 0x5e, 0xd4, 0x85 };
+
/* D0 Smart Log Page */
#define WDC_NVME_GET_VU_SMART_LOG_OPCODE 0xD0
#define WDC_NVME_VU_SMART_LOG_LEN 0x200
@@ -523,7 +534,6 @@ typedef enum
EOL_RRER = 108, /* Raw Read Error Rate */
} EOL_LOG_PAGE_C0_OFFSETS;
-
typedef struct __attribute__((__packed__)) _WDC_DE_VU_FILE_META_DATA
{
__u8 fileName[WDC_DE_FILE_NAME_SIZE];
@@ -800,6 +810,48 @@ struct wdc_bd_ca_log_format {
__u8 raw_value[7];
};
+#define READ 0
+#define WRITE 1
+#define TRIM 2
+#define RESERVED 3
+
+struct __attribute__((__packed__)) wdc_ssd_latency_monitor_log {
+ __u8 feature_status; /* 0x00 */
+ __u8 rsvd1; /* 0x01 */
+ __le16 active_bucket_timer; /* 0x02 */
+ __le16 active_bucket_timer_threshold; /* 0x04 */
+ __u8 active_threshold_a; /* 0x06 */
+ __u8 active_threshold_b; /* 0x07 */
+ __u8 active_threshold_c; /* 0x08 */
+ __u8 active_threshold_d; /* 0x09 */
+ __le16 active_latency_config; /* 0x0A */
+ __u8 active_latency_min_window; /* 0x0C */
+ __u8 rsvd2[0x13]; /* 0x0D */
+
+ __le32 active_bucket_counter[4][4] ; /* 0x20 - 0x5F */
+ __le64 active_latency_timestamp[4][3]; /* 0x60 - 0xBF */
+ __le16 active_measured_latency[4][3]; /* 0xC0 - 0xD7 */
+ __le16 active_latency_stamp_units; /* 0xD8 */
+ __u8 rsvd3[0x16]; /* 0xDA */
+
+ __le32 static_bucket_counter[4][4] ; /* 0xF0 - 0x12F */
+ __le64 static_latency_timestamp[4][3]; /* 0x130 - 0x18F */
+ __le16 static_measured_latency[4][3]; /* 0x190 - 0x1A7 */
+ __le16 static_latency_stamp_units; /* 0x1A8 */
+ __u8 rsvd4[0x16]; /* 0x1AA */
+
+ __le16 debug_log_trigger_enable; /* 0x1C0 */
+ __le16 debug_log_measured_latency; /* 0x1C2 */
+ __le64 debug_log_latency_stamp; /* 0x1C4 */
+ __le16 debug_log_ptr; /* 0x1CC */
+ __le16 debug_log_counter_trigger; /* 0x1CE */
+ __u8 debug_log_stamp_units; /* 0x1D0 */
+ __u8 rsvd5[0x1D]; /* 0x1D1 */
+
+ __le16 log_page_version; /* 0x1EE */
+ __u8 log_page_guid[0x10]; /* 0x1F0 */
+};
+
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 */
@@ -1238,6 +1290,10 @@ static __u64 wdc_get_drive_capabilities(int fd) {
WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID |
WDC_DRIVE_CAP_LOG_PAGE_DIR);
+ /* verify the 0xC3 log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_LATENCY_MON_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_C3_LOG_PAGE;
+
/* verify the 0xCA log page is supported */
if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true)
capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
@@ -1292,6 +1348,7 @@ static __u64 wdc_get_drive_capabilities(int fd) {
case WDC_NVME_SN650_DEV_ID_1:
case WDC_NVME_SN650_DEV_ID_2:
case WDC_NVME_SN650_DEV_ID_3:
+ case WDC_NVME_SN650_DEV_ID_4:
case WDC_NVME_SN450_DEV_ID_1:
case WDC_NVME_SN450_DEV_ID_2:
/* verify the 0xC0 log page is supported */
@@ -1386,6 +1443,10 @@ static __u64 wdc_get_enc_drive_capabilities(int fd) {
WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT |
WDC_DRIVE_CAP_RESIZE);
+ /* verify the 0xC3 log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_LATENCY_MON_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_C3_LOG_PAGE;
+
/* verify the 0xCB log page is supported */
if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID) == true)
capabilities |= WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY;
@@ -1972,6 +2033,7 @@ static int wdc_do_dump_e6(int fd, __u32 opcode,__u32 data_len,
static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int data_area)
{
struct nvme_telemetry_log_page_hdr *hdr;
+ struct nvme_id_ctrl ctrl;
size_t full_size, offset = WDC_TELEMETRY_HEADER_LENGTH;
int err = 0, output;
void *page_log;
@@ -2060,6 +2122,46 @@ static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int
case 3:
full_size = (le16_to_cpu(hdr->dalb3) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
break;
+ case 4:
+ err = nvme_identify_ctrl(fd, &ctrl);
+ if (err) {
+ perror("identify-ctrl");
+ goto close_output;
+ }
+
+ if (posix_memalign(&buf, getpagesize(), get_feat_buf_len(NVME_FEAT_HOST_BEHAVIOR))) {
+ fprintf(stderr, "can not allocate feature payload\n");
+ errno = ENOMEM;
+ err = -1;
+ goto close_output;
+ }
+ memset(buf, 0, get_feat_buf_len(NVME_FEAT_HOST_BEHAVIOR));
+
+ err = nvme_get_feature(fd, NVME_NSID_ALL, NVME_FEAT_HOST_BEHAVIOR, 0, 0,
+ 0, get_feat_buf_len(NVME_FEAT_HOST_BEHAVIOR), buf, &result);
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ perror("get-feature");
+ } else {
+ if ((ctrl.lpa & 0x40)) {
+ if (((unsigned char *)buf)[1] == 1)
+ full_size = (le32_to_cpu(hdr->dalb4) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
+ else {
+ fprintf(stderr, "Data area 4 unsupported, Host Behavior Support ETDAS not set to 1\n");
+ errno = EINVAL;
+ err = -1;
+ }
+ } else {
+ fprintf(stderr, "Data area 4 unsupported, bit 6 of Log Page Attributes not set\n");
+ errno = EINVAL;
+ err = -1;
+ }
+ }
+ free(buf);
+ if (err)
+ goto close_output;
+ break;
default:
fprintf(stderr, "%s: Invalid data area requested, data area = %d\n", __func__, data_area);
err = -EINVAL;
@@ -3496,6 +3598,170 @@ static int wdc_print_log(struct wdc_ssd_perf_stats *perf, int fmt)
return 0;
}
+static int wdc_convert_ts(time_t time, char *ts_buf)
+{
+ struct tm gmTimeInfo;
+ time_t time_Human, time_ms;
+ char buf[80];
+
+ time_Human = time/1000;
+ time_ms = time % 1000;
+
+ gmtime_r((const time_t *)&time_Human, &gmTimeInfo);
+
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &gmTimeInfo);
+ sprintf(ts_buf, "%s.%03ld GMT", buf, time_ms);
+
+ return 0;
+}
+
+static int wdc_print_latency_monitor_log_normal(int fd, struct wdc_ssd_latency_monitor_log *log_data)
+{
+ printf("Latency Monitor/C3 Log Page Data \n");
+ printf(" Controller : %s\n", devicename);
+ int err = -1, i, j;
+ struct nvme_id_ctrl ctrl;
+ char ts_buf[128];
+
+ err = nvme_identify_ctrl(fd, &ctrl);
+ if (!err)
+ printf(" Serial Number: %-.*s\n", (int)sizeof(ctrl.sn), ctrl.sn);
+ else {
+ fprintf(stderr, "ERROR : WDC : latency monitor read id ctrl failure, err = %d\n", err);
+ return err;
+ }
+
+ printf(" Feature Status 0x%x \n", log_data->feature_status);
+ printf(" Active Bucket Timer %d min \n", 5*le16_to_cpu(log_data->active_bucket_timer));
+ printf(" Active Bucket Timer Threshold %d min \n", 5*le16_to_cpu(log_data->active_bucket_timer_threshold));
+ printf(" Active Threshold A %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_a+1)));
+ printf(" Active Threshold B %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_b+1)));
+ printf(" Active Threshold C %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_c+1)));
+ printf(" Active Threshold D %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_d+1)));
+ printf(" Active Latency Config 0x%x \n", le16_to_cpu(log_data->active_latency_config));
+ printf(" Active Latency Minimum Window %d ms \n", 100*log_data->active_latency_min_window);
+ printf(" Active Latency Stamp Units %d \n", le16_to_cpu(log_data->active_latency_stamp_units));
+ printf(" Static Latency Stamp Units %d \n", le16_to_cpu(log_data->static_latency_stamp_units));
+ printf(" Debug Log Trigger Enable %d \n", le16_to_cpu(log_data->debug_log_trigger_enable));
+
+ printf(" Read Write Deallocate/Trim \n");
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Bucket Counter: Bucket %d %27d %27d %27d \n",
+ i, le32_to_cpu(log_data->active_bucket_counter[i][READ]), le32_to_cpu(log_data->active_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->active_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
+ i, le16_to_cpu(log_data->active_measured_latency[i][READ]), le16_to_cpu(log_data->active_measured_latency[i][WRITE]),
+ le16_to_cpu(log_data->active_measured_latency[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Latency Time Stamp: Bucket %d ", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1)
+ printf(" N/A ");
+ else {
+ wdc_convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Bucket Counter: Bucket %d %27d %27d %27d \n",
+ i, le32_to_cpu(log_data->static_bucket_counter[i][READ]), le32_to_cpu(log_data->static_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->static_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
+ i, le16_to_cpu(log_data->static_measured_latency[i][READ]), le16_to_cpu(log_data->static_measured_latency[i][WRITE]),
+ le16_to_cpu(log_data->static_measured_latency[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Latency Time Stamp: Bucket %d ", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1)
+ printf(" N/A ");
+ else {
+ wdc_convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static void wdc_print_latency_monitor_log_json(struct wdc_ssd_latency_monitor_log *log_data)
+{
+ int i, j;
+ char buf[128];
+ char *operation[3] = {"Read", "Write", "Trim"};
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Feature Status", log_data->feature_status);
+ json_object_add_value_int(root, "Active Bucket Timer", 5*le16_to_cpu(log_data->active_bucket_timer));
+ json_object_add_value_int(root, "Active Bucket Timer Threshold", 5*le16_to_cpu(log_data->active_bucket_timer_threshold));
+ json_object_add_value_int(root, "Active Threshold A", 5*le16_to_cpu(log_data->active_threshold_a+1));
+ json_object_add_value_int(root, "Active Threshold B", 5*le16_to_cpu(log_data->active_threshold_b+1));
+ json_object_add_value_int(root, "Active Threshold C", 5*le16_to_cpu(log_data->active_threshold_c+1));
+ json_object_add_value_int(root, "Active Threshold D", 5*le16_to_cpu(log_data->active_threshold_d+1));
+ json_object_add_value_int(root, "Active Latency Config", le16_to_cpu(log_data->active_latency_config));
+ json_object_add_value_int(root, "Active Lantency Minimum Window", 100*log_data->active_latency_min_window);
+ json_object_add_value_int(root, "Active Latency Stamp Units", le16_to_cpu(log_data->active_latency_stamp_units));
+ json_object_add_value_int(root, "Static Latency Stamp Units", le16_to_cpu(log_data->static_latency_stamp_units));
+ json_object_add_value_int(root, "Debug Log Trigger Enable", le16_to_cpu(log_data->debug_log_trigger_enable));
+
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Active Bucket Counter: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le32_to_cpu(log_data->active_bucket_counter[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Active Measured Latency: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le16_to_cpu(log_data->active_measured_latency[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Active Latency Time Stamp: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le64_to_cpu(log_data->active_latency_timestamp[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Static Bucket Counter: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le32_to_cpu(log_data->static_bucket_counter[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Static Measured Latency: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le16_to_cpu(log_data->static_measured_latency[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Static Latency Time Stamp: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le64_to_cpu(log_data->static_latency_timestamp[i][j]));
+ }
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
static void wdc_print_fb_ca_log_normal(struct wdc_ssd_ca_perf_stats *perf)
{
uint64_t converted = 0;
@@ -4351,10 +4617,12 @@ static void wdc_print_smart_cloud_attr_C0_normal(void *data)
printf(" SMART Cloud Attributes :- \n");
- printf(" Physical media units written %.0Lf\n",
- int128_to_double(&log_data[SCAO_PMUW]));
- printf(" Physical media units Read %.0Lf\n",
- int128_to_double(&log_data[SCAO_PMUR]));
+ printf(" Physical media units written - %"PRIu64" %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
+ printf(" Physical media units read - %"PRIu64" %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
printf(" Bad user nand blocks - Raw %"PRIu64"\n",
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF));
printf(" Bad user nand blocks - Normalized %d\n",
@@ -4432,10 +4700,14 @@ static void wdc_print_smart_cloud_attr_C0_json(void *data)
uint16_t smart_log_ver = 0;
root = json_create_object();
- json_object_add_value_float(root, "Physical media units written",
- int128_to_double(&log_data[SCAO_PMUW]));
- json_object_add_value_int(root, "Physical media units Read",
- int128_to_double(&log_data[SCAO_PMUR]));
+ json_object_add_value_int(root, "Physical media units written hi",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_int(root, "Physical media units written lo",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_int(root, "Physical media units read hi",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_int(root, "Physical media units read lo",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
json_object_add_value_uint(root, "Bad user nand blocks - Raw",
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF));
json_object_add_value_uint(root, "Bad user nand blocks - Normalized",
@@ -4595,7 +4867,7 @@ static int wdc_print_c0_eol_log(void *data, int fmt)
return 0;
}
-static int wdc_get_c0_log_page(int fd, char *format, int uuid_index)
+static int wdc_get_c0_log_page(int fd, char *format, int uuid_index, __u32 namespace_id)
{
int ret = 0;
int fmt = -1;
@@ -4639,8 +4911,15 @@ static int wdc_get_c0_log_page(int fd, char *format, int uuid_index)
return -1;
}
+ if (namespace_id == NVME_NSID_ALL) {
+ ret = namespace_id = nvme_get_nsid(fd);
+ if (ret < 0) {
+ namespace_id = NVME_NSID_ALL;
+ }
+ }
+
/* Get the 0xC0 log data */
- ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
+ ret = nvme_get_log14(fd, namespace_id, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
NVME_NO_LOG_LSP, 0, 0, false, uuid_index, 0, false, WDC_NVME_SMART_CLOUD_ATTR_LEN, data);
if (strcmp(format, "json"))
@@ -4687,7 +4966,7 @@ static int wdc_get_c0_log_page(int fd, char *format, int uuid_index)
}
/* Get the 0xC0 log data */
- ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
+ ret = nvme_get_log14(fd, NVME_NSID_ALL, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
NVME_NO_LOG_LSP, 0, 0, false, uuid_index, 0, false, WDC_NVME_EOL_STATUS_LOG_LEN, data);
if (strcmp(format, "json"))
@@ -4714,7 +4993,7 @@ static int wdc_get_c0_log_page(int fd, char *format, int uuid_index)
}
/* Get the 0xC0 log data */
- ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
+ ret = nvme_get_log(fd, NVME_NSID_ALL, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
false, NVME_NO_LOG_LSP, WDC_NVME_EOL_STATUS_LOG_LEN, data);
if (strcmp(format, "json"))
@@ -4740,7 +5019,7 @@ static int wdc_get_c0_log_page(int fd, char *format, int uuid_index)
}
/* Get the 0xC0 log data */
- ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE,
+ ret = nvme_get_log(fd, NVME_NSID_ALL, WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE,
false, NVME_NO_LOG_LSP, WDC_NVME_SMART_CLOUD_ATTR_LEN, data);
if (strcmp(format, "json"))
@@ -4768,6 +5047,23 @@ static int wdc_get_c0_log_page(int fd, char *format, int uuid_index)
return ret;
}
+static int wdc_print_latency_monitor_log(int fd, struct wdc_ssd_latency_monitor_log *log_data, int fmt)
+{
+ if (!log_data) {
+ fprintf(stderr, "ERROR : WDC : Invalid C3 log data buffer\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_latency_monitor_log_normal(fd, log_data);
+ break;
+ case JSON:
+ wdc_print_latency_monitor_log_json(log_data);
+ break;
+ }
+ return 0;
+}
+
static int wdc_print_fb_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt)
{
if (!perf) {
@@ -5030,6 +5326,77 @@ static int wdc_get_c1_log_page(int fd, char *format, uint8_t interval)
return ret;
}
+static int wdc_get_c3_log_page(int fd, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ int i;
+ struct wdc_ssd_latency_monitor_log *log_data;
+
+ if (!wdc_check_device(fd))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ if ((data = (__u8 *) malloc(sizeof(__u8) * WDC_LATENCY_MON_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_LATENCY_MON_LOG_BUF_LEN);
+
+ ret = nvme_get_log14(fd, NVME_NSID_ALL, WDC_LATENCY_MON_OPCODE,
+ NVME_NO_LOG_LSP, NVME_NO_LOG_LPO, 0, 0,
+ 0, 0, 0, WDC_LATENCY_MON_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+ if (ret == 0) {
+ log_data = (struct wdc_ssd_latency_monitor_log*)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != WDC_LATENCY_MON_VERSION) {
+ fprintf(stderr, "ERROR : WDC : invalid latency monitor version\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* check log page guid */
+ /* Verify GUID matches */
+ for (i=0; i<16; i++) {
+ if (wdc_lat_mon_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : WDC : Unknown GUID in C3 Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : WDC : Expected GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", wdc_lat_mon_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* parse the data */
+ wdc_print_latency_monitor_log(fd, log_data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read C3 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
static int wdc_get_d0_log_page(int fd, char *format)
{
int ret = 0;
@@ -5083,6 +5450,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
int fd;
const char *log_page_version = "Log Page Version: 0 = vendor, 1 = WDC";
const char *log_page_mask = "Log Page Mask, comma separated list: 0xC0, 0xC1, 0xCA, 0xD0";
+ const char *namespace_id = "desired namespace id";
int ret = 0;
int uuid_index = 0;
int page_mask = 0, num, i;
@@ -5094,6 +5462,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
char *output_format;
__u8 log_page_version;
char *log_page_mask;
+ __u32 namespace_id;
};
struct config cfg = {
@@ -5101,6 +5470,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
.output_format = "normal",
.log_page_version = 0,
.log_page_mask = "",
+ .namespace_id = NVME_NSID_ALL,
};
OPT_ARGS(opts) = {
@@ -5108,6 +5478,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version),
OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask),
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_END()
};
@@ -5170,7 +5541,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
if (((capabilities & WDC_DRIVE_CAP_C0_LOG_PAGE) == WDC_DRIVE_CAP_C0_LOG_PAGE) &&
(page_mask & WDC_C0_PAGE_MASK)) {
/* Get 0xC0 log page if possible. */
- ret = wdc_get_c0_log_page(fd, cfg.output_format, uuid_index);
+ ret = wdc_get_c0_log_page(fd, cfg.output_format, uuid_index, cfg.namespace_id);
if (ret)
fprintf(stderr, "ERROR : WDC : Failure reading the C0 Log Page, ret = %d\n", ret);
}
@@ -5201,6 +5572,48 @@ out:
return ret;
}
+static int wdc_get_latency_monitor_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve latency monitor log data.";
+ int fd;
+ int ret = 0;
+ __u64 capabilities = 0;
+
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ if ((capabilities & WDC_DRIVE_CAP_C3_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = wdc_get_c3_log_page(fd, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the C3 Log Page, ret = %d\n", ret);
+
+out:
+
+ return ret;
+}
+
static int wdc_do_clear_pcie_correctable_errors(int fd)
{
int ret;
@@ -7849,6 +8262,8 @@ static int wdc_capabilities(int argc, char **argv,
capabilities & WDC_DRIVE_CAP_C0_LOG_PAGE ? "Supported" : "Not Supported");
printf("--C1 Log Page : %s\n",
capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE ? "Supported" : "Not Supported");
+ printf("--C3 Log Page : %s\n",
+ capabilities & WDC_DRIVE_CAP_C3_LOG_PAGE ? "Supported" : "Not Supported");
printf("--CA Log Page : %s\n",
capabilities & WDC_DRIVE_CAP_CA_LOG_PAGE ? "Supported" : "Not Supported");
printf("--D0 Log Page : %s\n",
diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h
index 29ff6a0..e046007 100644
--- a/plugins/wdc/wdc-nvme.h
+++ b/plugins/wdc/wdc-nvme.h
@@ -4,7 +4,7 @@
#if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ)
#define WDC_NVME
-#define WDC_PLUGIN_VERSION "1.14.1"
+#define WDC_PLUGIN_VERSION "1.15.4"
#include "cmd.h"
PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERSION),
@@ -36,6 +36,7 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERS
ENTRY("capabilities", "WDC Device Capabilities", wdc_capabilities)
ENTRY("cloud-SSD-plugin-version", "WDC Cloud SSD Plugin Version", wdc_cloud_ssd_plugin_version)
ENTRY("vs-pcie-stats", "WDC VS PCIE Statistics", wdc_vs_pcie_stats)
+ ENTRY("get-latency-monitor-log", "WDC Get Latency Monitor Log Page", wdc_get_latency_monitor_log)
)
);