]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
plugins/wdc: enhanced SN861 device support
authorBrandon Paupore <brandon.paupore@wdc.com>
Wed, 11 Oct 2023 22:20:28 +0000 (17:20 -0500)
committerDaniel Wagner <wagi@monom.org>
Thu, 12 Oct 2023 06:21:09 +0000 (08:21 +0200)
This commit enables several commands to work with this device properly
and adds a new plugin command set-latency-monitor-feature.

Reviewed-by: Jeffrey Lien <jeff.lien@wdc.com>
Signed-off-by: Brandon Paupore <brandon.paupore@wdc.com>
Documentation/nvme-wdc-set-latency-monitor-feature.txt [new file with mode: 0644]
plugins/wdc/wdc-nvme.c
plugins/wdc/wdc-nvme.h

diff --git a/Documentation/nvme-wdc-set-latency-monitor-feature.txt b/Documentation/nvme-wdc-set-latency-monitor-feature.txt
new file mode 100644 (file)
index 0000000..1be7bf9
--- /dev/null
@@ -0,0 +1,113 @@
+nvme-wdc-set-latency-monitor-feature(1)
+=======================================
+
+NAME
+----
+nvme-wdc-set-latency-monitor-feature - Set NVMe WDC latency monitor feature options
+
+SYNOPSIS
+--------
+[verse]
+'nvme wdc set-latency-monitor-feature' <device>
+            [--active_bucket_timer_threshold=<NUM> | -t <NUM>]
+            [--active_threshold_a=<NUM> | -a <NUM>]
+            [--active_threshold_b=<NUM> | -b <NUM>]
+            [--active_threshold_c=<NUM> | -c <NUM>]
+            [--active_threshold_d=<NUM> | -d <NUM>]
+            [--active_latency_config=<NUM> | -f <NUM>]
+            [--active_latency_minimum_window=<NUM> | -w <NUM>]
+            [--debug_log_trigger_enable=<NUM> | -r <NUM>]
+            [--discard_debug_log=<NUM> | -l <NUM>]
+            [--latency_monitor_feature_enable=<NUM> | -e <NUM>]
+
+DESCRIPTION
+-----------
+For the NVMe device given, this command set the
+latency monitor feature options (if supported by the device).
+
+The <device> parameter is mandatory NVMe character device (ex: /dev/nvme0).
+
+Setting results can be checked with 'get-latency-monitor-log' command.
+
+On success it returns 0, error code otherwise.
+
+OPTIONS
+-------
+-t <NUM>::
+--active_bucket_timer_threshold=<NUM>::
+    The value that loads the Active Bucket Timer Threshold; default value is 07E0h.
+
+-a <NUM>::
+--active_threshold_a=<NUM>::
+    The value that loads into the Active Threshold A; default value is 05h.
+
+-b <NUM>::
+--active_threshold_b=<NUM>::
+    The value that loads into the Active Threshold B; default value is 13h.
+
+-c <NUM>::
+--active_threshold_c=<NUM>::
+    The value that loads into the Active Threshold C; default value is 1Eh.
+
+-d <NUM>::
+--active_threshold_d=<NUM>::
+    The value that loads into the Active Threshold D; default value is 2Eh.
+
+-f <NUM>::
+--active_latency_config=<NUM>::
+    The value that loads into the Active Latency Configuration. This configures how both the Active
+    Latency Stamp, and the Active Measured Latency Fields are updated on a per I/O command (Read,
+    Write, Deallocate) counter basis; default value is 0FFFh.
+
+-w <NUM>::
+--active_latency_minimum_window=<NUM>::
+    The value that loads into the Active Latency Minimum Window; default value is 0Ah.
+
+-r <NUM>::
+--debug_log_trigger_enable=<NUM>::
+    The value that loads into the Debug Log Trigger Enable; When set to 1b the first time the
+    bucket/counter combination is incremented a debug log is triggered. When cleared to 0b a
+    debug log will not be triggered when the bucket/counter combination is incremented.
+
+-l <NUM>::
+--discard_debug_log=<NUM>::
+    Discard Debug Log. When cleared to 00h the debug log, if it exists, will not be cleared.
+    When set to 01h the debug log will be discarded so another log can be triggered. All the fields in
+    the Set Features Data structure are valid. When set to 02h the debug log will be discarded so
+    another log can be triggered. None of the other fields of the Set Features Data structure are valid.
+
+-e <NUM>::
+--latency_monitor_feature_enable=<NUM>::
+    Latency Monitor Feature Enable; When set to 01h the Latency Monitor Feature is
+    enabled. When cleared to 00h the Latency Monitor Feature is disabled.
+
+EXAMPLES
+--------
+* Set NVMe WDC latency monitor feature options enabled with default value values:
++
+------------
+# nvme wdc set-latency-monitor-feature /dev/nvme0 -e 1
+------------
+* Set NVMe WDC latency monitor feature options disabled with default value values:
++
+------------
+# nvme wdc set-latency-monitor-feature /dev/nvme0 -e 0
+------------
+* Set NVMe WDC latency monitor feature options enabled with specific values:
++
+------------
+# nvme wdc set-latency-monitor-feature /dev/nvme0 --active_bucket_timer_threshold=1 \
+            --active_threshold_a=0x0 \
+            --active_threshold_b=0x1 \
+            --active_threshold_c=0x2 \
+            --active_threshold_d=0x3 \
+            --active_latency_config=0xfff \
+            --active_latency_minimum_window=0 \
+            --debug_log_trigger_enable=0 \
+            --discard_debug_log=0 \
+            --latency_monitor_feature_enable=0x1
+------------
+
+NVME
+----
+Part of the nvme-user suite.
\ No newline at end of file
index ae57e85f68cce717c455f61964928b22e3e87b9c..b66c1c759253789bca17eb747b686d947296cf4d 100644 (file)
 #define WDC_DRIVE_CAP_OCP_C4_LOG_PAGE                  0x0000004000000000
 #define WDC_DRIVE_CAP_OCP_C5_LOG_PAGE                  0x0000008000000000
 #define WDC_DRIVE_CAP_DEVICE_WAF                       0x0000010000000000
+#define WDC_DRIVE_CAP_SET_LATENCY_MONITOR              0x0000020000000000
+
 #define WDC_DRIVE_CAP_SMART_LOG_MASK                   (WDC_DRIVE_CAP_C0_LOG_PAGE | \
                                                         WDC_DRIVE_CAP_C1_LOG_PAGE | \
                                                         WDC_DRIVE_CAP_CA_LOG_PAGE | \
                                                         WDC_DRIVE_CAP_DUI | \
                                                         WDC_DRIVE_CAP_DUI_DATA | \
                                                         WDC_SN730B_CAP_VUC_LOG)
+
 /* SN730 Get Log Capabilities */
 #define SN730_NVME_GET_LOG_OPCODE                      0xc2
 #define SN730_GET_FULL_LOG_LENGTH                      0x00080009
@@ -447,6 +450,12 @@ static __u8 wdc_lat_mon_guid[WDC_C3_GUID_LENGTH] = {
 #define WDC_ENC_CRASH_DUMP_ID                          0xE4
 #define WDC_ENC_LOG_DUMP_ID                            0xE2
 
+/* OCP Log Page Directory Data Structure */
+#define BYTE_TO_BIT(byte)                              ((byte) * 8)
+
+/* Set latency monitor feature */
+#define NVME_FEAT_OCP_LATENCY_MONITOR                  0xC5
+
 enum _NVME_FEATURES_SELECT {
        FS_CURRENT                      = 0,
        FS_DEFAULT                      = 1,
@@ -657,6 +666,67 @@ enum {
        SCAO_LPG                = 496,  /* Log page GUID */
 };
 
+struct ocp_bad_nand_block_count {
+       __u64 raw : 48;
+       __u16 normalized : 16;
+};
+
+struct ocp_e2e_correction_count {
+       __u32 detected;
+       __u32 corrected;
+};
+
+struct ocp_user_data_erase_count {
+       __u32 maximum;
+       __u32 minimum;
+};
+
+struct ocp_thermal_status {
+       __u8 num_events;
+       __u8 current_status;
+};
+
+struct __packed ocp_dssd_specific_ver {
+       __u8 errata_ver;
+       __u16 point_ver;
+       __u16 minor_ver;
+       __u8 major_ver;
+};
+
+struct ocp_cloud_smart_log {
+       __u8 physical_media_units_written[16];
+       __u8 physical_media_units_read[16];
+       struct ocp_bad_nand_block_count bad_user_nand_blocks;
+       struct ocp_bad_nand_block_count bad_system_nand_blocks;
+       __u64 xor_recovery_count;
+       __u64 uncorrectable_read_error_count;
+       __u64 soft_ecc_error_count;
+       struct ocp_e2e_correction_count e2e_correction_counts;
+       __u8 system_data_percent_used;
+       __u64 refresh_counts : 56;
+       struct ocp_user_data_erase_count user_data_erase_counts;
+       struct ocp_thermal_status thermal_status;
+       struct ocp_dssd_specific_ver dssd_specific_ver;
+       __u64 pcie_correctable_error_count;
+       __u32 incomplete_shutdowns;
+       __u8 rsvd116[4];
+       __u8 percent_free_blocks;
+       __u8 rsvd121[7];
+       __u16 capacitor_health;
+       __u8 nvme_errata_ver;
+       __u8 rsvd131[5];
+       __u64 unaligned_io;
+       __u64 security_version_number;
+       __u64 total_nuse;
+       __u8 plp_start_count[16];
+       __u8 endurance_estimate[16];
+       __u64 pcie_link_retraining_cnt;
+       __u64 power_state_change_cnt;
+       __u8 rsvd208[286];
+       __u16 log_page_version;
+       __u8 log_page_guid[16];
+};
+
 static __u8 scao_guid[WDC_C0_GUID_LENGTH] = {
        0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF, 0xF2, 0xA4,
        0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF
@@ -794,6 +864,46 @@ static struct NVME_VU_DE_LOGPAGE_LIST deVULogPagesList[] = {
        { NVME_DE_LOGPAGE_C0, 0xC0, 512, "0xc0"}
 };
 
+enum {
+       WDC_NVME_ADMIN_VUC_OPCODE_D2 = 0xD2,
+       WDC_VUC_SUBOPCODE_VS_DRIVE_INFO_D2 = 0x0000010A,
+       WDC_VUC_SUBOPCODE_LOG_PAGE_DIR_D2 = 0x00000105,
+};
+
+enum {
+       NVME_LOG_NS_BASE                        = 0x80,
+       NVME_LOG_VS_BASE                        = 0xC0,
+};
+
+/*drive_info struct*/
+struct ocp_drive_info {
+       __u32 hw_revision;
+       __u32 ftl_unit_size;
+};
+
+/*get log page directory struct*/
+struct log_page_directory {
+       __u64 supported_lid_bitmap;
+       __u64 rsvd;
+       __u64 supported_ns_lid_bitmap;
+       __u64 supported_vs_lid_bitmap;
+};
+
+/*set latency monitor feature */
+struct __packed feature_latency_monitor {
+       __u16 active_bucket_timer_threshold;
+       __u8  active_threshold_a;
+       __u8  active_threshold_b;
+       __u8  active_threshold_c;
+       __u8  active_threshold_d;
+       __u16 active_latency_config;
+       __u8  active_latency_minimum_window;
+       __u16 debug_log_trigger_enable;
+       __u8  discard_debug_log;
+       __u8  latency_monitor_feature_enable;
+       __u8  reserved[4083];
+};
+
 static int wdc_get_serial_name(struct nvme_dev *dev, char *file, size_t len, const char *suffix);
 static int wdc_create_log_file(char *file, __u8 *drive_log_data, __u32 drive_log_length);
 static int wdc_do_clear_dump(struct nvme_dev *dev, __u8 opcode, __u32 cdw12);
@@ -1269,6 +1379,21 @@ static __u8 wdc_ocp_c5_guid[WDC_OCP_C5_GUID_LENGTH]    = { 0x2F, 0x72, 0x9C, 0x0
 #define WDC_REASON_ID_ENTRY_LEN                128
 #define WDC_REASON_ID_PATH_NAME                "/usr/local/nvmecli"
 
+const char *log_page_name[256] = {
+       [NVME_LOG_LID_ERROR]            = "Error Information",
+       [NVME_LOG_LID_SMART]            = "SMART / Health Information",
+       [NVME_LOG_LID_FW_SLOT]          = "Firmware Slot Information",
+       [NVME_LOG_LID_CHANGED_NS]       = "Changed Namespace List",
+       [NVME_LOG_LID_CMD_EFFECTS]      = "Command Supported and Effects",
+       [NVME_LOG_LID_TELEMETRY_HOST]   = "Telemetry Host-Initiated",
+       [NVME_LOG_LID_TELEMETRY_CTRL]   = "Telemetry Controller-Initiated",
+       [NVME_LOG_LID_SANITIZE]         = "Sanitize Status",
+       [WDC_LOG_ID_C0]                 = "Extended SMART Information",
+       [WDC_LOG_ID_C2]                 = "Firmware Activation History",
+       [WDC_LOG_ID_C3]                 = "Latency Monitor",
+       [WDC_LOG_ID_C4]                 = "Device Capabilities",
+       [WDC_LOG_ID_C5]                 = "Unsupported Requirements",
+};
 
 static double safe_div_fp(double numerator, double denominator)
 {
@@ -1668,16 +1793,20 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
 
                        break;
                case WDC_NVME_SN861_DEV_ID:
-                       capabilities |= (WDC_DRIVE_CAP_C0_LOG_PAGE | WDC_DRIVE_CAP_OCP_C1_LOG_PAGE |
-                               WDC_DRIVE_CAP_C3_LOG_PAGE | WDC_DRIVE_CAP_OCP_C4_LOG_PAGE |
-                               WDC_DRIVE_CAP_OCP_C5_LOG_PAGE);
-
-                       capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG |
-                                        WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT |
-                                        WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY |
-                                        WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG |
-                                        WDC_DRIVE_CAP_REASON_ID);
-
+                       capabilities |= (WDC_DRIVE_CAP_C0_LOG_PAGE |
+                               WDC_DRIVE_CAP_C3_LOG_PAGE |
+                               WDC_DRIVE_CAP_CA_LOG_PAGE |
+                               WDC_DRIVE_CAP_OCP_C4_LOG_PAGE |
+                               WDC_DRIVE_CAP_OCP_C5_LOG_PAGE |
+                               WDC_DRIVE_CAP_INTERNAL_LOG |
+                               WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 |
+                               WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE |
+                               WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY |
+                               WDC_DRIVE_CAP_INFO |
+                               WDC_DRIVE_CAP_CLOUD_SSD_VERSION |
+                               WDC_DRIVE_CAP_LOG_PAGE_DIR |
+                               WDC_DRIVE_CAP_SET_LATENCY_MONITOR);
+                       break;
                default:
                        capabilities = 0;
                }
@@ -3385,6 +3514,102 @@ free_buf:
        return ret;
 }
 
+static int dump_internal_logs(struct nvme_dev *dev, char *dir_name, int verbose)
+{
+       char file_path[128];
+       void *telemetry_log;
+       const size_t bs = 512;
+       struct nvme_telemetry_log *hdr;
+       size_t full_size, offset = bs;
+       int err, output;
+
+       if (verbose)
+               printf("NVMe Telemetry log...\n");
+
+       hdr = malloc(bs);
+       telemetry_log = malloc(bs);
+       if (!hdr || !telemetry_log) {
+               fprintf(stderr, "Failed to allocate %zu bytes for log: %s\n", bs, strerror(errno));
+               err = -ENOMEM;
+               goto free_mem;
+       }
+       memset(hdr, 0, bs);
+
+       sprintf(file_path, "%s/telemetry.bin", dir_name);
+       output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       if (output < 0) {
+               fprintf(stderr, "Failed to open output file %s: %s!\n", file_path, strerror(errno));
+               err = output;
+               goto free_mem;
+       }
+
+       struct nvme_get_log_args args = {
+               .lpo = 0,
+               .result = NULL,
+               .log = hdr,
+               .args_size = sizeof(args),
+               .fd = dev_fd(dev),
+               .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+               .lid = NVME_LOG_LID_TELEMETRY_HOST,
+               .len = bs,
+               .nsid = NVME_NSID_ALL,
+               .csi = NVME_CSI_NVM,
+               .lsi = NVME_LOG_LSI_NONE,
+               .lsp = NVME_LOG_TELEM_HOST_LSP_CREATE,
+               .uuidx = NVME_UUID_NONE,
+               .rae = true,
+               .ot = false,
+       };
+
+       err = nvme_get_log(&args);
+       if (err < 0)
+               perror("get-telemetry-log");
+       else if (err > 0) {
+               nvme_show_status(err);
+               fprintf(stderr, "Failed to acquire telemetry header %d!\n", err);
+               goto close_output;
+       }
+
+       err = write(output, (void *)hdr, bs);
+       if (err != bs) {
+               fprintf(stderr, "Failed to flush all data to file!\n");
+               goto close_output;
+       }
+
+       full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset;
+
+       while (offset != full_size) {
+               args.log = telemetry_log;
+               args.lpo = offset;
+               args.lsp = NVME_LOG_LSP_NONE;
+               err = nvme_get_log(&args);
+               if (err < 0) {
+                       perror("get-telemetry-log");
+                       break;
+               } else if (err > 0) {
+                       fprintf(stderr, "Failed to acquire full telemetry log!\n");
+                       nvme_show_status(err);
+                       break;
+               }
+
+               err = write(output, (void *)telemetry_log, bs);
+               if (err != bs) {
+                       fprintf(stderr, "Failed to flush all data to file!\n");
+                       break;
+               }
+               err = 0;
+               offset += bs;
+       }
+
+close_output:
+       close(output);
+free_mem:
+       free(hdr);
+       free(telemetry_log);
+
+       return err;
+}
+
 static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command,
                                  struct plugin *plugin)
 {
@@ -3397,6 +3622,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command
        char *type = "Telemetry type - NONE, HOST, or CONTROLLER. Currently only supported on the SN530, SN640, SN730, SN740, SN810, SN840 and ZN350 devices.";
        char *verbose = "Display more debug messages.";
        char f[PATH_MAX] = {0};
+       char fb[PATH_MAX/2] = {0};
        char fileSuffix[PATH_MAX] = {0};
        struct nvme_dev *dev;
        nvme_root_t r;
@@ -3405,6 +3631,9 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command
        UtilsTimeInfo             timeInfo;
        __u8                      timeStamp[MAX_PATH_LEN];
        __u64 capabilities = 0;
+       __u32 device_id, read_vendor_id;
+       char file_path[PATH_MAX/2] = {0};
+       char cmd_buf[PATH_MAX] = {0};
        int ret = -1;
 
        struct config {
@@ -3453,72 +3682,149 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command
                goto out;
        }
 
-       if (cfg.file) {
-               int verify_file;
+       ret = wdc_get_pci_ids(r, dev, &device_id, &read_vendor_id);
 
-               /* verify the passed in file name and path is valid before getting the dump data */
-               verify_file = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-               if (verify_file < 0) {
-                       fprintf(stderr, "ERROR: WDC: open: %s\n", strerror(errno));
-                       goto out;
+       if (device_id != WDC_NVME_SN861_DEV_ID) {
+               if (cfg.file) {
+                       int verify_file;
+
+                       /* verify file name and path is valid before getting dump data */
+                       verify_file = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+                       if (verify_file < 0) {
+                               fprintf(stderr, "ERROR: WDC: open: %s\n", strerror(errno));
+                               goto out;
+                       }
+                       close(verify_file);
+                       strncpy(f, cfg.file, PATH_MAX - 1);
+               } else {
+                       wdc_UtilsGetTime(&timeInfo);
+                       memset(timeStamp, 0, sizeof(timeStamp));
+                       wdc_UtilsSnprintf((char *)timeStamp, MAX_PATH_LEN,
+                               "%02u%02u%02u_%02u%02u%02u", timeInfo.year,
+                               timeInfo.month, timeInfo.dayOfMonth,
+                               timeInfo.hour, timeInfo.minute,
+                               timeInfo.second);
+                       snprintf(fileSuffix, PATH_MAX, "_internal_fw_log_%s", (char *)timeStamp);
+
+                       ret = wdc_get_serial_name(dev, f, PATH_MAX, fileSuffix);
+                       if (ret) {
+                               fprintf(stderr, "ERROR: WDC: failed to generate file name\n");
+                               goto out;
+                       }
                }
-               close(verify_file);
-               strncpy(f, cfg.file, PATH_MAX - 1);
-       } else {
-               wdc_UtilsGetTime(&timeInfo);
-               memset(timeStamp, 0, sizeof(timeStamp));
-               wdc_UtilsSnprintf((char *)timeStamp, MAX_PATH_LEN, "%02u%02u%02u_%02u%02u%02u",
-                       timeInfo.year, timeInfo.month, timeInfo.dayOfMonth,
-                       timeInfo.hour, timeInfo.minute, timeInfo.second);
-               snprintf(fileSuffix, PATH_MAX, "_internal_fw_log_%s", (char *)timeStamp);
 
-               ret = wdc_get_serial_name(dev, f, PATH_MAX, fileSuffix);
-               if (ret) {
-                       fprintf(stderr, "ERROR: WDC: failed to generate file name\n");
-                       goto out;
+               if (!cfg.file) {
+                       if (strlen(f) > PATH_MAX - 5) {
+                               fprintf(stderr, "ERROR: WDC: file name overflow\n");
+                               ret = -1;
+                               goto out;
+                       }
+                       strcat(f, ".bin");
                }
-       }
+               fprintf(stderr, "%s: filename = %s\n", __func__, f);
 
-       if (!cfg.file) {
-               if (strlen(f) > PATH_MAX - 5) {
-                       fprintf(stderr, "ERROR: WDC: file name overflow\n");
+               if (cfg.data_area) {
+                       if (cfg.data_area > 5 || cfg.data_area < 1) {
+                               fprintf(stderr, "ERROR: WDC: Data area must be 1-5\n");
+                               ret = -1;
+                               goto out;
+                       }
+               }
+
+               if (!cfg.type || !strcmp(cfg.type, "NONE") || !strcmp(cfg.type, "none")) {
+                       telemetry_type = WDC_TELEMETRY_TYPE_NONE;
+                       data_area = 0;
+               } else if (!strcmp(cfg.type, "HOST") || !strcmp(cfg.type, "host")) {
+                       telemetry_type = WDC_TELEMETRY_TYPE_HOST;
+                       telemetry_data_area = cfg.data_area;
+               } else if (!strcmp(cfg.type, "CONTROLLER") || !strcmp(cfg.type, "controller")) {
+                       telemetry_type = WDC_TELEMETRY_TYPE_CONTROLLER;
+                       telemetry_data_area = cfg.data_area;
+               } else {
+                       fprintf(stderr,
+                               "ERROR: WDC: Invalid type - Must be NONE, HOST or CONTROLLER\n");
                        ret = -1;
                        goto out;
                }
-               strcat(f, ".bin");
-       }
-       fprintf(stderr, "%s: filename = %s\n", __func__, f);
+       } else {
+               if (cfg.file) {
+                       strncpy(fb, cfg.file, PATH_MAX/2 - 8);
+               } else {
+                       wdc_UtilsGetTime(&timeInfo);
+                       memset(timeStamp, 0, sizeof(timeStamp));
+                       wdc_UtilsSnprintf((char *)timeStamp, MAX_PATH_LEN,
+                               "%02u%02u%02u_%02u%02u%02u", timeInfo.year,
+                               timeInfo.month, timeInfo.dayOfMonth,
+                               timeInfo.hour, timeInfo.minute,
+                               timeInfo.second);
+                       snprintf(fileSuffix, PATH_MAX, "_internal_fw_log_%s", (char *)timeStamp);
+
+                       ret = wdc_get_serial_name(dev, fb, PATH_MAX/2 - 7, fileSuffix);
+                       if (ret) {
+                               fprintf(stderr, "ERROR: WDC: failed to generate file name\n");
+                               goto out;
+                       }
+
+                       if (strlen(fb) > PATH_MAX/2 - 7) {
+                               fprintf(stderr, "ERROR: WDC: file name overflow\n");
+                               ret = -1;
+                               goto out;
+                       }
+               }
+               fprintf(stderr, "%s: filename = %s.tar.gz\n", __func__, fb);
+
 
-       if (cfg.data_area) {
-               if (cfg.data_area > 5 || cfg.data_area < 1) {
-                       fprintf(stderr, "ERROR: WDC: Data area must be 1-5\n");
+               memset(file_path, 0, sizeof(file_path));
+               if (snprintf(file_path, PATH_MAX/2 - 8, "%s.tar.gz", fb) >= PATH_MAX/2 - 8) {
+                       fprintf(stderr, "File path is too long!\n");
                        ret = -1;
                        goto out;
                }
-       }
-
-       if (!cfg.type || !strcmp(cfg.type, "NONE") || !strcmp(cfg.type, "none")) {
-               telemetry_type = WDC_TELEMETRY_TYPE_NONE;
-               data_area = 0;
-       } else if (!strcmp(cfg.type, "HOST") || !strcmp(cfg.type, "host")) {
-               telemetry_type = WDC_TELEMETRY_TYPE_HOST;
-               telemetry_data_area = cfg.data_area;
-       } else if (!strcmp(cfg.type, "CONTROLLER") || !strcmp(cfg.type, "controller")) {
-               telemetry_type = WDC_TELEMETRY_TYPE_CONTROLLER;
-               telemetry_data_area = cfg.data_area;
-       } else {
-               fprintf(stderr, "ERROR: WDC: Invalid type - Must be NONE, HOST or CONTROLLER\n");
-               ret = -1;
-               goto out;
+               if (access(file_path, F_OK) != -1) {
+                       fprintf(stderr, "Output file already exists!\n");
+                       ret = -EEXIST;
+                       goto out;
+               }
        }
 
        capabilities = wdc_get_drive_capabilities(r, dev);
        if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) {
-               if (!telemetry_data_area)
-                       telemetry_data_area = 3;       /* Set the default DA to 3 if not specified */
+               if (device_id != WDC_NVME_SN861_DEV_ID) {
+                       /* Set the default DA to 3 if not specified */
+                       if (!telemetry_data_area)
+                               telemetry_data_area = 3;
+
+                       ret = wdc_do_cap_diag(r, dev, f, xfer_size,
+                                       telemetry_type, telemetry_data_area);
+               } else {
+                       if (cfg.verbose)
+                               printf("Creating temp directory...\n");
+
+                       ret = mkdir(fb, 0666);
+                       if (ret) {
+                               fprintf(stderr, "Failed to create directory!\n");
+                               goto out;
+                       }
+
+                       ret = dump_internal_logs(dev, fb, cfg.verbose);
+                       if (ret < 0)
+                               perror("vs-internal-log");
+
+                       if (cfg.verbose)
+                               printf("Archiving...\n");
+
+                       if (snprintf(cmd_buf, PATH_MAX,
+                                    "tar --remove-files -czf %s %s",
+                                    file_path, fb) >= PATH_MAX) {
+                               fprintf(stderr, "Command buffer is too long!\n");
+                               ret = -1;
+                               goto out;
+                       }
 
-               ret = wdc_do_cap_diag(r, dev, f, xfer_size,
-                                 telemetry_type, telemetry_data_area);
+                       ret = system(cmd_buf);
+                       if (ret)
+                               fprintf(stderr, "Failed to create an archive file!\n");
+               }
                goto out;
        }
        if ((capabilities & WDC_DRIVE_CAP_DUI) == WDC_DRIVE_CAP_DUI) {
@@ -5018,7 +5324,9 @@ static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin)
 
 }
 
-static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u32 cust_id, __u32 vendor_id)
+static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries,
+                                               __u32 cust_id, __u32 vendor_id,
+                                               __u32 device_id)
 {
        int i, j;
        char previous_fw[9];
@@ -5032,7 +5340,9 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u
 
        if (data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) {
                printf("  Firmware Activate History Log\n");
-               if (cust_id == WDC_CUSTOMER_ID_0x1005 || vendor_id == WDC_NVME_SNDK_VID) {
+               if (cust_id == WDC_CUSTOMER_ID_0x1005 ||
+                   vendor_id == WDC_NVME_SNDK_VID ||
+                   device_id == WDC_NVME_SN861_DEV_ID) {
                        printf("           Power on Hour       Power Cycle     Previous    New\n");
                        printf("  Entry      hh:mm:ss             Count        Firmware    Firmware    Slot   Action  Result\n");
                        printf("  -----  -----------------  -----------------  ---------   ---------   -----  ------  -------\n");
@@ -5091,6 +5401,26 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u
                                                (int)((timestamp/1000)%60));
                                printf("%s", time_str);
                                printf("     ");
+                       } else if (device_id == WDC_NVME_SN861_DEV_ID) {
+                               printf("        ");
+                               char timestamp[20];
+                               __u64 hour;
+                               __u8 min;
+                               __u8 sec;
+                               __u64 timestamp_sec;
+
+                               timestamp_sec =
+                                       le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)
+                                       / 1000;
+                               hour = timestamp_sec / 3600;
+                               min = (timestamp_sec % 3600) / 60;
+                               sec = timestamp_sec % 60;
+
+                               sprintf(timestamp,
+                                       "%"PRIu64":%02"PRIu8":%02"PRIu8,
+                                       (uint64_t)hour, min, sec);
+                               printf("%-11s", timestamp);
+                               printf("     ");
                        } else {
                                printf("   ");
                                uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp));
@@ -5189,7 +5519,9 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u
        }
 }
 
-static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32 cust_id, __u32 vendor_id)
+static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries,
+                                             __u32 cust_id, __u32 vendor_id,
+                                             __u32 device_id)
 {
        struct json_object *root = json_create_object();
        int i, j;
@@ -5198,11 +5530,13 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32
        char commit_action_bin[8];
        char fail_str[32];
        char time_str[11];
+       char ext_time_str[20];
 
        memset((void *)previous_fw, 0, 9);
        memset((void *)new_fw, 0, 9);
        memset((void *)commit_action_bin, 0, 8);
        memset((void *)time_str, 0, 11);
+       memset((void *)ext_time_str, 0, 20);
        memset((void *)fail_str, 0, 11);
        char *null_fw = "--------";
        __u16 oldestEntryIdx = 0, entryIdx = 0;
@@ -5254,6 +5588,17 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32
                                sprintf((char *)time_str, "%04d:%02d:%02d", (int)((timestamp/(3600*1000))%24), (int)((timestamp/(1000*60))%60),
                                                (int)((timestamp/1000)%60));
                                json_object_add_value_string(root, "Power on Hour", time_str);
+                       } else if (device_id == WDC_NVME_SN861_DEV_ID) {
+                               __u64 timestamp_sec =
+                                       le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)
+                                       / 1000;
+
+                               sprintf((char *)ext_time_str,
+                                       "%"PRIu64":%02"PRIu8":%02"PRIu8,
+                                       (uint64_t)(__u64)(timestamp_sec/3600),
+                                       (__u8)((timestamp_sec%3600)/60),
+                                       (__u8)(timestamp_sec%60));
+                               json_object_add_value_string(root, "Power on Hour", ext_time_str);
                        } else {
                                uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp));
 
@@ -6526,8 +6871,6 @@ static int wdc_get_c0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format
        case WDC_NVME_SN560_DEV_ID_3:
                fallthrough;
        case WDC_NVME_SN550_DEV_ID:
-               fallthrough;
-       case WDC_NVME_SN861_DEV_ID:
                ret = wdc_get_c0_log_page_sn(r, dev, uuid_index, format, namespace_id, fmt);
                break;
        case WDC_NVME_ZN350_DEV_ID:
@@ -6711,7 +7054,9 @@ static int wdc_print_d0_log(struct wdc_ssd_d0_smart_log *perf, int fmt)
        return 0;
 }
 
-static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt, __u32 cust_id, __u32 vendor_id)
+static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt,
+                                       __u32 cust_id, __u32 vendor_id,
+                                       __u32 device_id)
 {
        if (!data) {
                fprintf(stderr, "ERROR: WDC: Invalid buffer to read fw activate history entries\n");
@@ -6720,10 +7065,12 @@ static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt, __
 
        switch (fmt) {
        case NORMAL:
-               wdc_print_fw_act_history_log_normal(data, num_entries, cust_id, vendor_id);
+               wdc_print_fw_act_history_log_normal(data, num_entries, cust_id,
+                                                   vendor_id, device_id);
                break;
        case JSON:
-               wdc_print_fw_act_history_log_json(data, num_entries, cust_id, vendor_id);
+               wdc_print_fw_act_history_log_json(data, num_entries, cust_id,
+                                                 vendor_id, device_id);
                break;
        }
        return 0;
@@ -7249,6 +7596,235 @@ static int wdc_get_d0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format
        return ret;
 }
 
+static long double le_to_float(__u8 *data, int byte_len)
+{
+       long double result = 0;
+       int i;
+
+       for (i = 0; i < byte_len; i++) {
+               result *= 256;
+               result += data[15 - i];
+       }
+
+       return result;
+}
+
+static void stringify_log_page_guid(__u8 *guid, char *buf)
+{
+       char *ptr = buf;
+       int i;
+
+       memset(buf, 0, sizeof(char) * 19);
+
+       ptr += sprintf(ptr, "0x");
+       for (i = 0; i < 16; i++)
+               ptr += sprintf(ptr, "%x", guid[15 - i]);
+}
+
+static const char *const cloud_smart_log_thermal_status[] = {
+       [0x00] = "unthrottled",
+       [0x01] = "first_level",
+       [0x02] = "second_level",
+       [0x03] = "third_level",
+};
+
+static const char *stringify_cloud_smart_log_thermal_status(__u8 status)
+{
+       if (status < ARRAY_SIZE(cloud_smart_log_thermal_status) &&
+           cloud_smart_log_thermal_status[status])
+               return cloud_smart_log_thermal_status[status];
+       return "unrecognized";
+}
+
+static void show_cloud_smart_log_json(struct ocp_cloud_smart_log *log)
+{
+       struct json_object *root;
+       struct json_object *bad_user_nand_blocks;
+       struct json_object *bad_system_nand_blocks;
+       struct json_object *e2e_correction_counts;
+       struct json_object *user_data_erase_counts;
+       struct json_object *thermal_status;
+       struct json_object *dssd_specific_ver;
+       char buf[2 * sizeof(log->log_page_guid) + 3];
+
+       bad_user_nand_blocks = json_create_object();
+       json_object_add_value_uint(bad_user_nand_blocks, "normalized",
+                                  le16_to_cpu(log->bad_user_nand_blocks.normalized));
+       json_object_add_value_uint(bad_user_nand_blocks, "raw",
+                                  le64_to_cpu(log->bad_user_nand_blocks.raw));
+
+       bad_system_nand_blocks = json_create_object();
+       json_object_add_value_uint(bad_system_nand_blocks, "normalized",
+                                  le16_to_cpu(log->bad_system_nand_blocks.normalized));
+       json_object_add_value_uint(bad_system_nand_blocks, "raw",
+                                  le64_to_cpu(log->bad_system_nand_blocks.raw));
+
+       e2e_correction_counts = json_create_object();
+       json_object_add_value_uint(e2e_correction_counts, "corrected",
+                                  le32_to_cpu(log->e2e_correction_counts.corrected));
+       json_object_add_value_uint(e2e_correction_counts, "detected",
+                                  le32_to_cpu(log->e2e_correction_counts.detected));
+
+       user_data_erase_counts = json_create_object();
+       json_object_add_value_uint(user_data_erase_counts, "minimum",
+                                  le32_to_cpu(log->user_data_erase_counts.minimum));
+       json_object_add_value_uint(user_data_erase_counts, "maximum",
+                                  le32_to_cpu(log->user_data_erase_counts.maximum));
+
+       thermal_status = json_create_object();
+       json_object_add_value_string(thermal_status, "current_status",
+               stringify_cloud_smart_log_thermal_status(log->thermal_status.current_status));
+       json_object_add_value_uint(thermal_status, "num_events",
+                                  log->thermal_status.num_events);
+
+       dssd_specific_ver = json_create_object();
+       json_object_add_value_uint(dssd_specific_ver, "major_ver",
+                                  log->dssd_specific_ver.major_ver);
+       json_object_add_value_uint(dssd_specific_ver, "minor_ver",
+                                  le16_to_cpu(log->dssd_specific_ver.minor_ver));
+       json_object_add_value_uint(dssd_specific_ver, "point_ver",
+                                  le16_to_cpu(log->dssd_specific_ver.point_ver));
+       json_object_add_value_uint(dssd_specific_ver, "errata_ver",
+                                  log->dssd_specific_ver.errata_ver);
+
+       root = json_create_object();
+       json_object_add_value_uint64(root, "physical_media_units_written",
+                                    le_to_float(log->physical_media_units_written, 16));
+       json_object_add_value_uint64(root, "physical_media_units_read",
+                                    le_to_float(log->physical_media_units_read, 16));
+       json_object_add_value_object(root, "bad_user_nand_blocks",
+                                    bad_user_nand_blocks);
+       json_object_add_value_object(root, "bad_system_nand_blocks",
+                                    bad_system_nand_blocks);
+       json_object_add_value_uint(root, "xor_recovery_count",
+                                  le64_to_cpu(log->xor_recovery_count));
+       json_object_add_value_uint(root, "uncorrectable_read_error_count",
+                                  le64_to_cpu(log->uncorrectable_read_error_count));
+       json_object_add_value_uint(root, "soft_ecc_error_count",
+                                  le64_to_cpu(log->soft_ecc_error_count));
+       json_object_add_value_object(root, "e2e_correction_counts",
+                                    e2e_correction_counts);
+       json_object_add_value_uint(root, "system_data_percent_used",
+                                  log->system_data_percent_used);
+       json_object_add_value_uint(root, "refresh_counts",
+                                  le64_to_cpu(log->refresh_counts));
+       json_object_add_value_object(root, "user_data_erase_counts",
+                                    user_data_erase_counts);
+       json_object_add_value_object(root, "thermal_status", thermal_status);
+       json_object_add_value_object(root, "dssd_specific_ver",
+                                    dssd_specific_ver);
+       json_object_add_value_uint(root, "pcie_correctable_error_count",
+                                  le64_to_cpu(log->pcie_correctable_error_count));
+       json_object_add_value_uint(root, "incomplete_shutdowns",
+                                  le32_to_cpu(log->incomplete_shutdowns));
+       json_object_add_value_uint(root, "percent_free_blocks",
+                                  log->percent_free_blocks);
+       json_object_add_value_uint(root, "capacitor_health",
+                                  le16_to_cpu(log->capacitor_health));
+       sprintf(buf, "%c", log->nvme_errata_ver);
+       json_object_add_value_string(root, "nvme_errata_version", buf);
+       json_object_add_value_uint(root, "unaligned_io",
+                                  le64_to_cpu(log->unaligned_io));
+       json_object_add_value_uint(root, "security_version_number",
+                                  le64_to_cpu(log->security_version_number));
+       json_object_add_value_uint(root, "total_nuse",
+                                  le64_to_cpu(log->total_nuse));
+       json_object_add_value_uint64(root, "plp_start_count",
+                                    le_to_float(log->plp_start_count, 16));
+       json_object_add_value_uint64(root, "endurance_estimate",
+                                    le_to_float(log->endurance_estimate, 16));
+       json_object_add_value_uint(root, "pcie_link_retraining_count",
+                                  le64_to_cpu(log->pcie_link_retraining_cnt));
+       json_object_add_value_uint(root, "power_state_change_count",
+                                  le64_to_cpu(log->power_state_change_cnt));
+       json_object_add_value_uint(root, "log_page_version",
+                                  le16_to_cpu(log->log_page_version));
+       stringify_log_page_guid(log->log_page_guid, buf);
+       json_object_add_value_string(root, "log_page_guid", buf);
+
+       json_print_object(root, NULL);
+       printf("\n");
+       json_free_object(root);
+}
+
+static void show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, struct nvme_dev *dev)
+{
+       char buf[2 * sizeof(log->log_page_guid) + 3];
+
+       printf("Smart Extended Log for NVME device:%s\n", dev->name);
+       printf("Physical Media Units Written                 : %'.0Lf\n",
+              le_to_float(log->physical_media_units_written, 16));
+       printf("Physical Media Units Read                    : %'.0Lf\n",
+              le_to_float(log->physical_media_units_read, 16));
+       printf("Bad User NAND Blocks (Normalized)            : %" PRIu16 "%%\n",
+              le16_to_cpu(log->bad_user_nand_blocks.normalized));
+       printf("Bad User NAND Blocks (Raw)                   : %" PRIu64 "\n",
+              le64_to_cpu(log->bad_user_nand_blocks.raw));
+       printf("Bad System NAND Blocks (Normalized)          : %" PRIu16 "%%\n",
+              le16_to_cpu(log->bad_system_nand_blocks.normalized));
+       printf("Bad System NAND Blocks (Raw)                 : %" PRIu64 "\n",
+              le64_to_cpu(log->bad_system_nand_blocks.raw));
+       printf("XOR Recovery Count                           : %" PRIu64 "\n",
+              le64_to_cpu(log->xor_recovery_count));
+       printf("Uncorrectable Read Error Count               : %" PRIu64 "\n",
+              le64_to_cpu(log->uncorrectable_read_error_count));
+       printf("Soft ECC Error Count                         : %" PRIu64 "\n",
+              le64_to_cpu(log->soft_ecc_error_count));
+       printf("End to End Correction Counts (Corrected)     : %" PRIu32 "\n",
+              le32_to_cpu(log->e2e_correction_counts.corrected));
+       printf("End to End Correction Counts (Detected)      : %" PRIu32 "\n",
+              le32_to_cpu(log->e2e_correction_counts.detected));
+       printf("System Data %% Used                           : %" PRIu8 "%%\n",
+              log->system_data_percent_used);
+       printf("Refresh Counts                               : %" PRIu64 "\n",
+              le64_to_cpu(log->refresh_counts));
+       printf("User Data Erase Counts (Minimum)             : %" PRIu32 "\n",
+              le32_to_cpu(log->user_data_erase_counts.minimum));
+       printf("User Data Erase Counts (Maximum)             : %" PRIu32 "\n",
+              le32_to_cpu(log->user_data_erase_counts.maximum));
+       printf("Thermal Throttling Status (Current Status)   : %s\n",
+              stringify_cloud_smart_log_thermal_status(log->thermal_status.current_status));
+       printf("Thermal Throttling Status (Number of Events) : %" PRIu8 "\n",
+              log->thermal_status.num_events);
+       printf("NVMe Major Version                           : %" PRIu8 "\n",
+              log->dssd_specific_ver.major_ver);
+       printf("     Minor Version                           : %" PRIu16 "\n",
+       le16_to_cpu(log->dssd_specific_ver.minor_ver));
+       printf("     Point Version                           : %" PRIu16 "\n",
+       le16_to_cpu(log->dssd_specific_ver.point_ver));
+       printf("     Errata Version                          : %" PRIu8 "\n",
+              log->dssd_specific_ver.errata_ver);
+       printf("PCIe Correctable Error Count                 : %" PRIu64 "\n",
+              le64_to_cpu(log->pcie_correctable_error_count));
+       printf("Incomplete Shutdowns                         : %" PRIu32 "\n",
+              le32_to_cpu(log->incomplete_shutdowns));
+       printf("%% Free Blocks                                : %" PRIu8 "%%\n",
+              log->percent_free_blocks);
+       printf("Capacitor Health                             : %" PRIu16 "%%\n",
+              le16_to_cpu(log->capacitor_health));
+       printf("NVMe Errata Version                          : %c\n",
+              log->nvme_errata_ver);
+       printf("Unaligned IO                                 : %" PRIu64 "\n",
+              le64_to_cpu(log->unaligned_io));
+       printf("Security Version Number                      : %" PRIu64 "\n",
+              le64_to_cpu(log->security_version_number));
+       printf("Total NUSE                                   : %" PRIu64 "\n",
+              le64_to_cpu(log->total_nuse));
+       printf("PLP Start Count                              : %'.0Lf\n",
+              le_to_float(log->plp_start_count, 16));
+       printf("Endurance Estimate                           : %'.0Lf\n",
+              le_to_float(log->endurance_estimate, 16));
+       printf("PCIe Link Retraining Count                   : %" PRIu64 "\n",
+              le64_to_cpu(log->pcie_link_retraining_cnt));
+       printf("Power State Change Count                     : %" PRIu64 "\n",
+              le64_to_cpu(log->power_state_change_cnt));
+       printf("Log Page Version                             : %" PRIu16 "\n",
+              le16_to_cpu(log->log_page_version));
+       stringify_log_page_guid(log->log_page_guid, buf);
+       printf("Log Page GUID                                : %s\n", buf);
+       printf("\n\n");
+}
+
 static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
                struct plugin *plugin)
 {
@@ -7262,8 +7838,10 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
        int ret = 0;
        int uuid_index = 0;
        int page_mask = 0, num, i;
+       int fmt = -1;
        int log_page_list[16];
        __u64 capabilities = 0;
+       __u32 device_id, read_vendor_id;
 
        struct config {
                uint8_t interval;
@@ -7283,7 +7861,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
 
        OPT_ARGS(opts) = {
                OPT_UINT("interval",          'i', &cfg.interval,         interval),
-               OPT_FMT("output-format",      'o', &cfg.output_format,    "Output Format: normal|json"),
+               OPT_FMT("output-format",      'o', &cfg.output_format,    output_format),
                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),
@@ -7331,8 +7909,9 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
        if (!page_mask)
                fprintf(stderr, "ERROR: WDC: Unknown log page mask - %s\n", cfg.log_page_mask);
 
-       capabilities = wdc_get_drive_capabilities(r, dev);
+       ret = wdc_get_pci_ids(r, dev, &device_id, &read_vendor_id);
 
+       capabilities = wdc_get_drive_capabilities(r, dev);
        if (!(capabilities & WDC_DRIVE_CAP_SMART_LOG_MASK)) {
                fprintf(stderr, "ERROR: WDC: unsupported device for this command\n");
                ret = -1;
@@ -7342,13 +7921,55 @@ 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(r, dev, cfg.output_format,
-                                         uuid_index, cfg.namespace_id);
-               if (ret)
-                       fprintf(stderr, "ERROR: WDC: Failure reading the C0 Log Page, ret = %d\n", ret);
+               if (device_id != WDC_NVME_SN861_DEV_ID) {
+                       ret = wdc_get_c0_log_page(r, dev, cfg.output_format,
+                                               uuid_index, cfg.namespace_id);
+                       if (ret)
+                               fprintf(stderr,
+                                       "ERROR: WDC: Failure reading the C0 Log Page, ret = %d\n",
+                                       ret);
+               } else {
+                       struct ocp_cloud_smart_log log;
+                       char buf[2 * sizeof(log.log_page_guid) + 3];
+
+                       fmt = validate_output_format(cfg.output_format);
+                       if (fmt < 0) {
+                               fprintf(stderr, "Invalid output format: %s\n", cfg.output_format);
+                               goto out;
+                       }
+
+                       ret = nvme_get_log_simple(dev_fd(dev),
+                               WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID,
+                               sizeof(log), &log);
+                       if (!ret) {
+                               char *ptr = buf;
+                               int i;
+                               __u8 *guid = log.log_page_guid;
+
+                               memset(buf, 0, sizeof(char) * 19);
+
+                               ptr += sprintf(ptr, "0x");
+                               for (i = 0; i < 16; i++)
+                                       ptr += sprintf(ptr, "%x", guid[15 - i]);
+                               if (strcmp(buf, "0xafd514c97c6f4f9ca4f2bfea2810afc5"))
+                                       fprintf(stderr, "Invalid GUID: %s\n", buf);
+                               else {
+                                       if (fmt & BINARY)
+                                               d_raw((unsigned char *)&log, sizeof(log));
+                                       else if (fmt & JSON)
+                                               show_cloud_smart_log_json(&log);
+                                       else
+                                               show_cloud_smart_log_normal(&log, dev);
+                               }
+                       } else if (ret > 0) {
+                               nvme_show_status(ret);
+                       } else {
+                               perror("vs-smart-add-log");
+                       }
+               }
        }
        if (((capabilities & (WDC_DRIVE_CAP_CA_LOG_PAGE)) == (WDC_DRIVE_CAP_CA_LOG_PAGE)) &&
-           (page_mask & WDC_CA_PAGE_MASK)) {
+           (page_mask & WDC_CA_PAGE_MASK) && device_id != WDC_NVME_SN861_DEV_ID) {
                /* Get the CA Log Page */
                ret = wdc_get_ca_log_page(r, dev, cfg.output_format);
                if (ret)
@@ -8135,7 +8756,7 @@ static int wdc_get_fw_act_history(nvme_root_t r, struct nvme_dev *dev,
                if ((fw_act_history_hdr->num_entries > 0) &&
                    (fw_act_history_hdr->num_entries <= WDC_MAX_NUM_ACT_HIST_ENTRIES)) {
                        ret = wdc_print_fw_act_history_log(data, fw_act_history_hdr->num_entries,
-                                                          fmt, 0, 0);
+                                                          fmt, 0, 0, 0);
                } else if (!fw_act_history_hdr->num_entries) {
                        fprintf(stderr, "INFO: WDC: No FW Activate History entries found.\n");
                        ret = 0;
@@ -8176,7 +8797,7 @@ static int wdc_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev,
        int ret = 0;
        int fmt = -1;
        __u8 *data;
-       __u32 cust_id;
+       __u32 cust_id = 0;
        struct wdc_fw_act_history_log_format_c2 *fw_act_history_log;
        __u32 tot_entries = 0, num_entries = 0;
        __u32 vendor_id = 0, device_id = 0;
@@ -8214,15 +8835,20 @@ static int wdc_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev,
 
                if (tot_entries > 0) {
                        /* get the FW customer id */
-                       cust_id = wdc_get_fw_cust_id(r, dev);
-                       if (cust_id == WDC_INVALID_CUSTOMER_ID) {
-                               fprintf(stderr, "%s: ERROR: WDC: invalid customer id\n", __func__);
-                               ret = -1;
-                               goto freeData;
+                       if (device_id != WDC_NVME_SN861_DEV_ID) {
+                               cust_id = wdc_get_fw_cust_id(r, dev);
+                               if (cust_id == WDC_INVALID_CUSTOMER_ID) {
+                                       fprintf(stderr,
+                                               "%s: ERROR: WDC: invalid customer id\n",
+                                               __func__);
+                                       ret = -1;
+                                       goto freeData;
+                               }
                        }
                        num_entries = (tot_entries < WDC_MAX_NUM_ACT_HIST_ENTRIES) ? tot_entries :
                                WDC_MAX_NUM_ACT_HIST_ENTRIES;
-                       ret = wdc_print_fw_act_history_log(data, num_entries, fmt, cust_id, vendor_id);
+                       ret = wdc_print_fw_act_history_log(data, num_entries,
+                               fmt, cust_id, vendor_id, device_id);
                } else  {
                        fprintf(stderr, "INFO: WDC: No FW Activate History entries found.\n");
                        ret = 0;
@@ -9584,6 +10210,99 @@ static const char *nvme_log_id_to_string(__u8 log_id)
        }
 }
 
+static void __json_log_page_directory(struct log_page_directory *directory)
+{
+       __u32 bitmap_idx;
+       __u8  log_id;
+       struct json_object *root;
+       struct json_object *entries;
+
+       root = json_create_object();
+
+       entries = json_create_array();
+       json_object_add_value_array(root, "Entries", entries);
+
+       for (bitmap_idx = 0; bitmap_idx < BYTE_TO_BIT(sizeof(__u64)); bitmap_idx++) {
+               log_id = bitmap_idx;
+               if (!log_page_name[log_id])
+                       continue;
+               if (directory->supported_lid_bitmap & (1ULL << bitmap_idx)) {
+                       struct json_object *json_entry = json_create_object();
+
+                       json_object_add_value_uint(json_entry, "Log ID", log_id);
+                       json_object_add_value_string(json_entry, "Log Page Name",
+                                                    log_page_name[log_id]);
+
+                       json_array_add_value_object(entries, json_entry);
+               }
+       }
+
+       for (bitmap_idx = 0; bitmap_idx < BYTE_TO_BIT(sizeof(__u64)); bitmap_idx++) {
+               log_id = NVME_LOG_NS_BASE + bitmap_idx;
+               if (!log_page_name[log_id])
+                       continue;
+               if (directory->supported_ns_lid_bitmap & (1ULL << bitmap_idx)) {
+                       struct json_object *json_entry = json_create_object();
+
+                       json_object_add_value_uint(json_entry, "Log ID", log_id);
+                       json_object_add_value_string(json_entry, "Log Page Name",
+                                                    log_page_name[log_id]);
+
+                       json_array_add_value_object(entries, json_entry);
+               }
+       }
+
+       for (bitmap_idx = 0; bitmap_idx < BYTE_TO_BIT(sizeof(__u64)); bitmap_idx++) {
+               log_id = NVME_LOG_VS_BASE + bitmap_idx;
+               if (!log_page_name[log_id])
+                       continue;
+               if (directory->supported_vs_lid_bitmap & (1ULL << bitmap_idx)) {
+                       struct json_object *json_entry = json_create_object();
+
+                       json_object_add_value_uint(json_entry, "Log ID", log_id);
+                       json_object_add_value_string(json_entry, "Log Page Name",
+                                                    log_page_name[log_id]);
+
+                       json_array_add_value_object(entries, json_entry);
+               }
+       }
+
+       json_print_object(root, NULL);
+       printf("\n");
+       json_free_object(root);
+}
+
+
+static void __show_log_page_directory(struct log_page_directory *directory)
+{
+       __u32 bitmap_idx;
+       __u8  log_id;
+
+       for (bitmap_idx = 0; bitmap_idx < BYTE_TO_BIT(sizeof(__u64)); bitmap_idx++) {
+               if (directory->supported_lid_bitmap & (1ULL << bitmap_idx)) {
+                       log_id = bitmap_idx;
+                       if (log_page_name[log_id])
+                               printf("0x%02X: %s\n", log_id, log_page_name[log_id]);
+               }
+       }
+
+       for (bitmap_idx = 0; bitmap_idx < BYTE_TO_BIT(sizeof(__u64)); bitmap_idx++) {
+               if (directory->supported_ns_lid_bitmap & (1ULL << bitmap_idx)) {
+                       log_id = NVME_LOG_NS_BASE + bitmap_idx;
+                       if (log_page_name[log_id])
+                               printf("0x%02X: %s\n", log_id, log_page_name[log_id]);
+               }
+       }
+
+       for (bitmap_idx = 0; bitmap_idx < BYTE_TO_BIT(sizeof(__u64)); bitmap_idx++) {
+               if (directory->supported_vs_lid_bitmap & (1ULL << bitmap_idx)) {
+                       log_id = NVME_LOG_VS_BASE + bitmap_idx;
+                       if (log_page_name[log_id])
+                               printf("0x%02X: %s\n", log_id, log_page_name[log_id]);
+               }
+       }
+}
+
 static int wdc_log_page_directory(int argc, char **argv, struct command *command,
                struct plugin *plugin)
 {
@@ -9630,53 +10349,94 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command
                ret = -1;
        } else {
                ret = wdc_get_pci_ids(r, dev, &device_id, &read_vendor_id);
-               log_id = (device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) ?
-                       WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 : WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE;
-               /* verify the 0xC2 Device Manageability log page is supported */
-               if (wdc_nvme_check_supported_log_page(r, dev, log_id) == false) {
-                       fprintf(stderr, "%s: ERROR: WDC: 0x%x Log Page not supported\n", __func__, log_id);
-                       ret = -1;
-                       goto out;
-               }
-
-               if (get_dev_mgment_cbs_data(r, dev, WDC_C2_LOG_PAGES_SUPPORTED_ID, (void *)&cbs_data)) {
-                       if (cbs_data) {
-                               printf("Log Page Directory\n");
-                               /* print the supported pages */
-                               if (!strcmp(cfg.output_format, "normal")) {
-                                       for (i = 0; i < le32_to_cpu(cbs_data->length); i++)
-                                               printf("0x%x  - %s\n", cbs_data->data[i],
-                                                      nvme_log_id_to_string(cbs_data->data[i]));
-                               } else if (!strcmp(cfg.output_format, "binary")) {
-                                       d((__u8 *)cbs_data->data, le32_to_cpu(cbs_data->length), 16,
-                                         1);
-                               } else if (!strcmp(cfg.output_format, "json")) {
-                                       struct json_object *root = json_create_object();
-
-                                       for (i = 0; i < le32_to_cpu(cbs_data->length); i++)
-                                               json_object_add_value_int(root,
-                                                                         nvme_log_id_to_string(cbs_data->data[i]),
-                                                                         cbs_data->data[i]);
+               log_id = (device_id == WDC_NVME_ZN350_DEV_ID ||
+                         device_id == WDC_NVME_ZN350_DEV_ID_1) ?
+                       WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 :
+                       WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE;
+
+               if (device_id != WDC_NVME_SN861_DEV_ID) {
+                       /* verify the 0xC2 Device Manageability log page is supported */
+                       if (wdc_nvme_check_supported_log_page(r, dev, log_id) == false) {
+                               fprintf(stderr,
+                                       "%s: ERROR: WDC: 0x%x Log Page not supported\n",
+                                       __func__, log_id);
+                               ret = -1;
+                               goto out;
+                       }
 
-                                       json_print_object(root, NULL);
-                                       printf("\n");
-                                       json_free_object(root);
-                               } else {
-                                       fprintf(stderr,
-                                               "%s: ERROR: WDC: Invalid format, format = %s\n",
-                                               __func__, cfg.output_format);
+                       if (!get_dev_mgment_cbs_data(r, dev,
+                                                   WDC_C2_LOG_PAGES_SUPPORTED_ID,
+                                                   (void *)&cbs_data)) {
+                               fprintf(stderr,
+                                       "%s: ERROR: WDC: 0xC2 Log Page entry ID 0x%x not found\n",
+                                       __func__, WDC_C2_LOG_PAGES_SUPPORTED_ID);
+                               ret = -1;
+                               goto out;
+                       }
+                       if (!cbs_data) {
+                               fprintf(stderr, "%s: ERROR: WDC: NULL_data ptr\n", __func__);
+                               ret = -1;
+                               goto out;
+                       }
+                       printf("Log Page Directory\n");
+                       /* print the supported pages */
+                       if (!strcmp(cfg.output_format, "normal")) {
+                               for (i = 0; i < le32_to_cpu(cbs_data->length); i++)
+                                       printf("0x%x  - %s\n", cbs_data->data[i],
+                                              nvme_log_id_to_string(cbs_data->data[i]));
+                       } else if (!strcmp(cfg.output_format, "binary")) {
+                               d((__u8 *)cbs_data->data,
+                                 le32_to_cpu(cbs_data->length), 16, 1);
+                       } else if (!strcmp(cfg.output_format, "json")) {
+                               struct json_object *root = json_create_object();
+
+                               for (i = 0; i < le32_to_cpu(cbs_data->length); i++) {
+                                       json_object_add_value_int(root,
+                                               nvme_log_id_to_string(cbs_data->data[i]),
+                                               cbs_data->data[i]);
                                }
 
-                               free(cbs_data);
+                               json_print_object(root, NULL);
+                               printf("\n");
+                               json_free_object(root);
                        } else {
-                               fprintf(stderr, "%s: ERROR: WDC: NULL_data ptr\n", __func__);
+                               fprintf(stderr,
+                                       "%s: ERROR: WDC: Invalid format, format = %s\n",
+                                       __func__, cfg.output_format);
                        }
+
+                       free(cbs_data);
                } else {
-                       fprintf(stderr, "%s: ERROR: WDC: 0xC2 Log Page entry ID 0x%x not found\n",
-                               __func__, WDC_C2_LOG_PAGES_SUPPORTED_ID);
-               }
+                       struct log_page_directory *dir;
+                       void *data = NULL;
+                       __u32 result;
+
+                       if (posix_memalign(&data, getpagesize(), 512)) {
+                               fprintf(stderr,
+                                       "can not allocate log page directory payload\n");
+                               ret = ENOMEM;
+                               goto out;
+                       }
 
+                       dir = (struct log_page_directory *)data;
+                       ret = nvme_admin_passthru(dev_fd(dev), WDC_NVME_ADMIN_VUC_OPCODE_D2, 0, 0,
+                                       0, 0, 0, 8,
+                                       0, WDC_VUC_SUBOPCODE_LOG_PAGE_DIR_D2, 0, 0, 0,
+                                       32, data, 0, NULL,
+                                       0, &result);
 
+                       if (!ret) {
+                               if (!strcmp(cfg.output_format, "binary"))
+                                       d_raw((unsigned char *)data, 32);
+                               else if (!strcmp(cfg.output_format, "json"))
+                                       __json_log_page_directory(dir);
+                               else
+                                       __show_log_page_directory(dir);
+                       } else {
+                               fprintf(stderr, "NVMe Status:%s(%x)\n",
+                                       nvme_status_to_string(ret, false), ret);
+                       }
+               }
        }
 
 out:
@@ -10448,6 +11208,9 @@ static int wdc_vs_drive_info(int argc, char **argv,
        char rev_str[16] = { 0 };
        uint32_t read_device_id = -1, read_vendor_id = -1;
        struct __packed wdc_nvme_ext_smart_log * ext_smart_log_ptr = NULL;
+       struct ocp_drive_info info;
+       __u32 data_len = 0;
+       unsigned int num_dwords = 0;
 
        struct config {
                char *output_format;
@@ -10624,6 +11387,50 @@ static int wdc_vs_drive_info(int argc, char **argv,
                                json_free_object(root);
                        }
 
+                       break;
+               case WDC_NVME_SN861_DEV_ID:
+                       data_len = sizeof(info);
+                       num_dwords = data_len / 4;
+                       if (data_len % 4 != 0)
+                               num_dwords += 1;
+
+                       ret = nvme_admin_passthru(dev_fd(dev),
+                                                 WDC_NVME_ADMIN_VUC_OPCODE_D2,
+                                                 0, 0, 0, 0, 0, num_dwords, 0,
+                                                 WDC_VUC_SUBOPCODE_VS_DRIVE_INFO_D2,
+                                                 0, 0, 0, data_len, &info, 0,
+                                                 NULL, 0, NULL);
+
+                       if (!ret) {
+                               __u16 hw_rev_major, hw_rev_minor;
+
+                               hw_rev_major = le32_to_cpu(info.hw_revision) / 10;
+                               hw_rev_minor = le32_to_cpu(info.hw_revision) % 10;
+                               if (fmt == NORMAL) {
+                                       printf("HW Revision   : %" PRIu32 ".%" PRIu32 "\n",
+                                              hw_rev_major, hw_rev_minor);
+                                       printf("FTL Unit Size : %" PRIu32 "\n",
+                                              le32_to_cpu(info.ftl_unit_size));
+                               } else if (fmt == JSON) {
+                                       char buf[20];
+
+                                       root = json_create_object();
+
+                                       memset((void *)buf, 0, 20);
+                                       sprintf(buf, "%" PRIu32 ".%" PRIu32,
+                                               hw_rev_major, hw_rev_minor);
+
+                                       json_object_add_value_string(root,
+                                               "hw_revision", buf);
+                                       json_object_add_value_uint(root,
+                                               "ftl_unit_size",
+                                               le32_to_cpu(info.ftl_unit_size));
+
+                                       json_print_object(root, NULL);
+                                       printf("\n");
+                                       json_free_object(root);
+                               }
+                       }
                        break;
                default:
                        fprintf(stderr, "ERROR: WDC: unsupported device for this command\n");
@@ -10858,6 +11665,8 @@ static int wdc_capabilities(int argc, char **argv, struct command *command, stru
               capabilities & WDC_DRIVE_CAP_HW_REV_LOG_PAGE ? "Supported" : "Not Supported");
        printf("vs-device_waf                 : %s\n",
               capabilities & WDC_DRIVE_CAP_DEVICE_WAF ? "Supported" : "Not Supported");
+       printf("set-latency-monitor-feature   : %s\n",
+              capabilities & WDC_DRIVE_CAP_SET_LATENCY_MONITOR ? "Supported" : "Not Supported");
        printf("capabilities                  : Supported\n");
        nvme_free_tree(r);
        dev_close(dev);
@@ -11228,3 +12037,159 @@ static int wdc_enc_get_nic_log(struct nvme_dev *dev, __u8 log_id, __u32 xfer_siz
        free(dump_data);
        return ret;
 }
+
+//------------------------------------------------------------------------------------
+// Description: set latency monitor feature
+//
+int wdc_set_latency_monitor_feature(int argc, char **argv, struct command *cmd,
+                                   struct plugin *plugin)
+{
+       const char *desc = "Set Latency Monitor feature.";
+
+       uint64_t capabilities = 0;
+       struct nvme_dev *dev;
+       nvme_root_t r;
+       int ret;
+       __u32 result;
+       struct feature_latency_monitor buf = {0,};
+
+       const char *active_bucket_timer_threshold =
+               "This is the value that loads the Active Bucket Timer Threshold.";
+       const char *active_threshold_a =
+               "This is the value that loads into the Active Threshold A.";
+       const char *active_threshold_b =
+               "This is the value that loads into the Active Threshold B.";
+       const char *active_threshold_c =
+               "This is the value that loads into the Active Threshold C.";
+       const char *active_threshold_d =
+               "This is the value that loads into the Active Threshold D.";
+       const char *active_latency_config =
+               "This is the value that loads into the Active Latency Configuration.";
+       const char *active_latency_minimum_window =
+               "This is the value that loads into the Active Latency Minimum Window.";
+       const char *debug_log_trigger_enable =
+               "This is the value that loads into the Debug Log Trigger Enable.";
+       const char *discard_debug_log = "Discard Debug Log.";
+       const char *latency_monitor_feature_enable = "Latency Monitor Feature Enable.";
+
+       struct config {
+               __u16 active_bucket_timer_threshold;
+               __u8 active_threshold_a;
+               __u8 active_threshold_b;
+               __u8 active_threshold_c;
+               __u8 active_threshold_d;
+               __u16 active_latency_config;
+               __u8 active_latency_minimum_window;
+               __u16 debug_log_trigger_enable;
+               __u8 discard_debug_log;
+               __u8 latency_monitor_feature_enable;
+       };
+
+       struct config cfg = {
+               .active_bucket_timer_threshold = 0x7E0,
+               .active_threshold_a = 0x5,
+               .active_threshold_b = 0x13,
+               .active_threshold_c = 0x1E,
+               .active_threshold_d = 0x2E,
+               .active_latency_config = 0xFFF,
+               .active_latency_minimum_window = 0xA,
+               .debug_log_trigger_enable = 0,
+               .discard_debug_log = 0,
+               .latency_monitor_feature_enable = 0x7,
+       };
+
+       OPT_ARGS(opts) = {
+               OPT_UINT("active_bucket_timer_threshold", 't',
+                       &cfg.active_bucket_timer_threshold,
+                       active_bucket_timer_threshold),
+               OPT_UINT("active_threshold_a", 'a', &cfg.active_threshold_a,
+                       active_threshold_a),
+               OPT_UINT("active_threshold_b", 'b', &cfg.active_threshold_b,
+                       active_threshold_b),
+               OPT_UINT("active_threshold_c", 'c', &cfg.active_threshold_c,
+                       active_threshold_c),
+               OPT_UINT("active_threshold_d", 'd', &cfg.active_threshold_d,
+                       active_threshold_d),
+               OPT_UINT("active_latency_config", 'f',
+                       &cfg.active_latency_config, active_latency_config),
+               OPT_UINT("active_latency_minimum_window", 'w',
+                       &cfg.active_latency_minimum_window,
+                       active_latency_minimum_window),
+               OPT_UINT("debug_log_trigger_enable", 'r',
+                       &cfg.debug_log_trigger_enable, debug_log_trigger_enable),
+               OPT_UINT("discard_debug_log", 'l', &cfg.discard_debug_log,
+                       discard_debug_log),
+               OPT_UINT("latency_monitor_feature_enable", 'e',
+                       &cfg.latency_monitor_feature_enable,
+                       latency_monitor_feature_enable),
+               OPT_END()
+       };
+
+       ret = parse_and_open(&dev, argc, argv, desc, opts);
+
+       if (ret < 0)
+               return ret;
+
+       /* get capabilities */
+       r = nvme_scan(NULL);
+       wdc_check_device(r, dev);
+       capabilities = wdc_get_drive_capabilities(r, dev);
+
+       if (!(capabilities & WDC_DRIVE_CAP_SET_LATENCY_MONITOR)) {
+               fprintf(stderr, "ERROR: WDC: unsupported device for this command\n");
+               return -1;
+       }
+
+       memset(&buf, 0, sizeof(struct feature_latency_monitor));
+
+       buf.active_bucket_timer_threshold = cfg.active_bucket_timer_threshold;
+       buf.active_threshold_a = cfg.active_threshold_a;
+       buf.active_threshold_b = cfg.active_threshold_b;
+       buf.active_threshold_c = cfg.active_threshold_c;
+       buf.active_threshold_d = cfg.active_threshold_d;
+       buf.active_latency_config = cfg.active_latency_config;
+       buf.active_latency_minimum_window = cfg.active_latency_minimum_window;
+       buf.debug_log_trigger_enable = cfg.debug_log_trigger_enable;
+       buf.discard_debug_log = cfg.discard_debug_log;
+       buf.latency_monitor_feature_enable = cfg.latency_monitor_feature_enable;
+
+       struct nvme_set_features_args args = {
+               .args_size = sizeof(args),
+               .fd = dev_fd(dev),
+               .fid = NVME_FEAT_OCP_LATENCY_MONITOR,
+               .nsid = 0,
+               .cdw12 = 0,
+               .save = 1,
+               .data_len = sizeof(struct feature_latency_monitor),
+               .data = (void *)&buf,
+               .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+               .result = &result,
+       };
+
+       ret = nvme_set_features(&args);
+
+       if (ret < 0) {
+               perror("set-feature");
+       } else if (!ret) {
+               printf("NVME_FEAT_OCP_LATENCY_MONITOR: 0x%02x\n",
+                       NVME_FEAT_OCP_LATENCY_MONITOR);
+               printf("active bucket timer threshold: 0x%x\n",
+                       buf.active_bucket_timer_threshold);
+               printf("active threshold a: 0x%x\n", buf.active_threshold_a);
+               printf("active threshold b: 0x%x\n", buf.active_threshold_b);
+               printf("active threshold c: 0x%x\n", buf.active_threshold_c);
+               printf("active threshold d: 0x%x\n", buf.active_threshold_d);
+               printf("active latency config: 0x%x\n", buf.active_latency_config);
+               printf("active latency minimum window: 0x%x\n",
+                       buf.active_latency_minimum_window);
+               printf("debug log trigger enable: 0x%x\n",
+                       buf.debug_log_trigger_enable);
+               printf("discard debug log: 0x%x\n", buf.discard_debug_log);
+               printf("latency monitor feature enable: 0x%x\n",
+                       buf.latency_monitor_feature_enable);
+       } else if (ret > 0)
+               fprintf(stderr, "NVMe Status:%s(%x)\n",
+                               nvme_status_to_string(ret, false), ret);
+
+       return ret;
+}
index 2f39ae6b5c9b6a126e630b593c20ace7f2d3ec2b..c5d2b09740c90f5738f39a6cf5fc7a105352ee41 100644 (file)
@@ -80,6 +80,9 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERS
                ENTRY("vs-device-waf",
                        "WDC Calculate Device Write Amplication Factor",
                        wdc_vs_device_waf)
+               ENTRY("set-latency-monitor-feature",
+                       "WDC set Latency Monitor feature",
+                       wdc_set_latency_monitor_feature)
        )
 );