#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
#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,
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
{ 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);
#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)
{
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;
}
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)
{
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;
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 {
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) {
}
-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];
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");
(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));
}
}
-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;
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;
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));
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:
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");
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;
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)
{
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;
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),
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;
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)
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;
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;
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;
}
}
+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)
{
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:
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;
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");
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);
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;
+}