diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-12-24 07:57:54 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-12-24 07:57:54 +0000 |
commit | 66e4b69042cd3b44acd42f1fad2109180c1bc48b (patch) | |
tree | 8bd8e664ae856167566375357963062e8112f181 /plugins | |
parent | Releasing debian version 2.5-1. (diff) | |
download | nvme-cli-66e4b69042cd3b44acd42f1fad2109180c1bc48b.tar.xz nvme-cli-66e4b69042cd3b44acd42f1fad2109180c1bc48b.zip |
Merging upstream version 2.7.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins')
48 files changed, 3948 insertions, 685 deletions
diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index 8539e18..2a221f8 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -56,7 +56,7 @@ static int fdp_configs(int argc, char **argv, struct command *cmd, if (err) return err; - err = flags = validate_output_format(cfg.output_format); + err = validate_output_format(cfg.output_format, &flags); if (flags < 0) goto out; @@ -137,7 +137,7 @@ static int fdp_usage(int argc, char **argv, struct command *cmd, struct plugin * if (err) return err; - err = flags = validate_output_format(cfg.output_format); + err = validate_output_format(cfg.output_format, &flags); if (flags < 0) goto out; @@ -208,7 +208,7 @@ static int fdp_stats(int argc, char **argv, struct command *cmd, struct plugin * if (err) return err; - err = flags = validate_output_format(cfg.output_format); + err = validate_output_format(cfg.output_format, &flags); if (flags < 0) goto out; @@ -269,7 +269,7 @@ static int fdp_events(int argc, char **argv, struct command *cmd, struct plugin if (err) return err; - err = flags = validate_output_format(cfg.output_format); + err = validate_output_format(cfg.output_format, &flags); if (flags < 0) goto out; @@ -328,7 +328,7 @@ static int fdp_status(int argc, char **argv, struct command *cmd, struct plugin if (err) return err; - err = flags = validate_output_format(cfg.output_format); + err = validate_output_format(cfg.output_format, &flags); if (flags < 0) goto out; diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 82e190f..0272dea 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -296,7 +296,7 @@ static int huawei_list(int argc, char **argv, struct command *command, struct huawei_list_item *list_items; unsigned int i, n, ret; unsigned int huawei_num = 0; - int fmt; + enum nvme_print_flags fmt; const char *desc = "Retrieve basic information for the given huawei device"; struct config { char *output_format; @@ -315,9 +315,9 @@ static int huawei_list(int argc, char **argv, struct command *command, if (ret) return ret; - fmt = validate_output_format(cfg.output_format); - if (fmt != JSON && fmt != NORMAL) - return -EINVAL; + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0 || (fmt != JSON && fmt != NORMAL)) + return ret; n = scandir("/dev", &devices, nvme_namespace_filter, alphasort); if (n <= 0) diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index e62c85d..378ecc0 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -1065,7 +1065,7 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct media_version[1] = (data[3] << 8) | data[2]; if (err) - goto close_fd; + goto close_dev; if (media_version[0] == 1000) { __u32 thresholds[OPTANE_V1000_BUCKET_LEN] = {0}; @@ -1086,9 +1086,9 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct }; err = nvme_get_features(&args); if (err) { - fprintf(stderr, "Quering thresholds failed. "); + fprintf(stderr, "Querying thresholds failed. "); nvme_show_status(err); - goto close_fd; + goto close_dev; } /* Update bucket thresholds to be printed */ @@ -1124,7 +1124,7 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct sizeof(stats)); } -close_fd: +close_dev: dev_close(dev); return err; } diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index c0a70ee..b215125 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -646,7 +646,7 @@ static int glp_high_latency_show_bar(FILE *fdi, int print) } /* - * High latency log page definiton + * High latency log page definition * Total 32 bytes */ struct log_page_high_latency { @@ -1197,3 +1197,646 @@ static int mb_set_lat_stats(int argc, char **argv, dev_close(dev); return err; } + +// Global definitions + +static inline int K2C(int k) // KELVINS_2_CELSIUS +{ + return (k - 273); +}; + +// Global ID definitions + +enum { + // feature ids + FID_LATENCY_FEATURE = 0xd0, + + // log ids + LID_SMART_LOG_ADD = 0xca, + LID_LATENCY_STATISTICS = 0xd0, + LID_HIGH_LATENCY_LOG = 0xd1, + LID_PERFORMANCE_STATISTICS = 0xd2, +}; + +// smart-log-add + +struct smart_log_add_item { + uint32_t index; + char *attr; +}; + +struct __packed wear_level { + __le16 min; + __le16 max; + __le16 avg; +}; + +struct __packed smart_log_add_item_12 { + uint8_t id; + uint8_t rsvd[2]; + uint8_t norm; + uint8_t rsvd1; + union { + struct wear_level wear_level; // 0xad + struct temp_since_born { // 0xe7 + __le16 max; + __le16 min; + __le16 curr; + } temp_since_born; + struct power_consumption { // 0xe8 + __le16 max; + __le16 min; + __le16 curr; + } power_consumption; + struct temp_since_power_on { // 0xaf + __le16 max; + __le16 min; + __le16 curr; + } temp_since_power_on; + uint8_t raw[6]; + }; + uint8_t rsvd2; +}; + +struct __packed smart_log_add_item_10 { + uint8_t id; + uint8_t norm; + union { + struct wear_level wear_level; // 0xad + uint8_t raw[6]; + }; + uint8_t rsvd[2]; +}; + +struct smart_log_add { + union { + union { + struct smart_log_add_v0 { + struct smart_log_add_item_12 program_fail_count; + struct smart_log_add_item_12 erase_fail_count; + struct smart_log_add_item_12 wear_leveling_count; + struct smart_log_add_item_12 end_to_end_error_count; + struct smart_log_add_item_12 crc_error_count; + struct smart_log_add_item_12 timed_workload_media_wear; + struct smart_log_add_item_12 timed_workload_host_reads; + struct smart_log_add_item_12 timed_workload_timer; + struct smart_log_add_item_12 thermal_throttle_status; + struct smart_log_add_item_12 retry_buffer_overflow_counter; + struct smart_log_add_item_12 pll_lock_loss_count; + struct smart_log_add_item_12 nand_bytes_written; + struct smart_log_add_item_12 host_bytes_written; + struct smart_log_add_item_12 system_area_life_remaining; + struct smart_log_add_item_12 nand_bytes_read; + struct smart_log_add_item_12 temperature; + struct smart_log_add_item_12 power_consumption; + struct smart_log_add_item_12 power_on_temperature; + struct smart_log_add_item_12 power_loss_protection; + struct smart_log_add_item_12 read_fail_count; + struct smart_log_add_item_12 thermal_throttle_time; + struct smart_log_add_item_12 flash_error_media_count; + } v0; + + struct smart_log_add_item_12 v0_raw[22]; + }; + + union { + struct smart_log_add_v2 { + struct smart_log_add_item_12 program_fail_count; + struct smart_log_add_item_12 erase_fail_count; + struct smart_log_add_item_12 wear_leveling_count; + struct smart_log_add_item_12 end_to_end_error_count; + struct smart_log_add_item_12 crc_error_count; + struct smart_log_add_item_12 timed_workload_media_wear; + struct smart_log_add_item_12 timed_workload_host_reads; + struct smart_log_add_item_12 timed_workload_timer; + struct smart_log_add_item_12 thermal_throttle_status; + struct smart_log_add_item_12 lifetime_write_amplification; + struct smart_log_add_item_12 pll_lock_loss_count; + struct smart_log_add_item_12 nand_bytes_written; + struct smart_log_add_item_12 host_bytes_written; + struct smart_log_add_item_12 system_area_life_remaining; + struct smart_log_add_item_12 firmware_update_count; + struct smart_log_add_item_12 dram_cecc_count; + struct smart_log_add_item_12 dram_uecc_count; + struct smart_log_add_item_12 xor_pass_count; + struct smart_log_add_item_12 xor_fail_count; + struct smart_log_add_item_12 xor_invoked_count; + struct smart_log_add_item_12 inflight_read_io_cmd; + struct smart_log_add_item_12 flash_error_media_count; + struct smart_log_add_item_12 nand_bytes_read; + struct smart_log_add_item_12 temp_since_born; + struct smart_log_add_item_12 power_consumption; + struct smart_log_add_item_12 temp_since_bootup; + struct smart_log_add_item_12 thermal_throttle_time; + } v2; + + struct smart_log_add_item_12 v2_raw[27]; + }; + + union { + struct smart_log_add_v3 { + struct smart_log_add_item_10 program_fail_count; + struct smart_log_add_item_10 erase_fail_count; + struct smart_log_add_item_10 wear_leveling_count; + struct smart_log_add_item_10 ext_e2e_err_count; + struct smart_log_add_item_10 crc_err_count; + struct smart_log_add_item_10 nand_bytes_written; + struct smart_log_add_item_10 host_bytes_written; + struct smart_log_add_item_10 reallocated_sector_count; + struct smart_log_add_item_10 uncorrectable_sector_count; + struct smart_log_add_item_10 nand_uecc_detection; + struct smart_log_add_item_10 nand_xor_correction; + struct smart_log_add_item_10 gc_count; + struct smart_log_add_item_10 dram_uecc_detection_count; + struct smart_log_add_item_10 sram_uecc_detection_count; + struct smart_log_add_item_10 internal_raid_recovery_fail_count; + struct smart_log_add_item_10 inflight_cmds; + struct smart_log_add_item_10 internal_e2e_err_count; + struct smart_log_add_item_10 die_fail_count; + struct smart_log_add_item_10 wear_leveling_execution_count; + struct smart_log_add_item_10 read_disturb_count; + struct smart_log_add_item_10 data_retention_count; + struct smart_log_add_item_10 capacitor_health; + } v3; + + struct smart_log_add_item_10 v3_raw[24]; + }; + + uint8_t raw[512]; + }; +}; + +static void smart_log_add_v0_print(struct smart_log_add_item_12 *item, int item_count) +{ + static const struct smart_log_add_item items[0xff] = { + [0xab] = {0, "program_fail_count" }, + [0xac] = {1, "erase_fail_count" }, + [0xad] = {2, "wear_leveling_count" }, + [0xb8] = {3, "end_to_end_error_count" }, + [0xc7] = {4, "crc_error_count" }, + [0xe2] = {5, "timed_workload_media_wear" }, + [0xe3] = {6, "timed_workload_host_reads" }, + [0xe4] = {7, "timed_workload_timer" }, + [0xea] = {8, "thermal_throttle_status" }, + [0xf0] = {9, "retry_buffer_overflow_counter"}, + [0xf3] = {10, "pll_lock_loss_count" }, + [0xf4] = {11, "nand_bytes_written" }, + [0xf5] = {12, "host_bytes_written" }, + [0xf6] = {13, "system_area_life_remaining" }, + [0xfa] = {14, "nand_bytes_read" }, + [0xe7] = {15, "temperature" }, + [0xe8] = {16, "power_consumption" }, + [0xaf] = {17, "power_on_temperature" }, + [0xec] = {18, "power_loss_protection" }, + [0xf2] = {19, "read_fail_count" }, + [0xeb] = {20, "thermal_throttle_time" }, + [0xed] = {21, "flash_error_media_count" }, + }; + + for (int i = 0; i < item_count; i++, item++) { + if (item->id == 0) + continue; + + printf("%#-12" PRIx8 "%-36s%-12d", item->id, items[item->id].attr, item->norm); + switch (item->id) { + case 0xad: + printf("min: %d, max: %d, avg: %d\n", + le16_to_cpu(item->wear_level.min), + le16_to_cpu(item->wear_level.max), + le16_to_cpu(item->wear_level.avg)); + break; + case 0xe7: + printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", + K2C(le16_to_cpu(item->temp_since_born.max)), + le16_to_cpu(item->temp_since_born.max), + K2C(le16_to_cpu(item->temp_since_born.min)), + le16_to_cpu(item->temp_since_born.min), + K2C(le16_to_cpu(item->temp_since_born.curr)), + le16_to_cpu(item->temp_since_born.curr)); + break; + case 0xe8: + printf("max: %d, min: %d, curr: %d\n", + le16_to_cpu(item->power_consumption.max), + le16_to_cpu(item->power_consumption.min), + le16_to_cpu(item->power_consumption.curr)); + break; + case 0xaf: + printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", + K2C(le16_to_cpu(item->temp_since_power_on.max)), + le16_to_cpu(item->temp_since_power_on.max), + K2C(le16_to_cpu(item->temp_since_power_on.min)), + le16_to_cpu(item->temp_since_power_on.min), + K2C(le16_to_cpu(item->temp_since_power_on.curr)), + le16_to_cpu(item->temp_since_power_on.curr)); + break; + default: + printf("%" PRIu64 "\n", int48_to_long(item->raw)); + break; + } + } +} + +static void smart_log_add_v2_print(struct smart_log_add_item_12 *item, int item_count) +{ + static const struct smart_log_add_item items[0xff] = { + [0xab] = {0, "program_fail_count" }, + [0xac] = {1, "erase_fail_count" }, + [0xad] = {2, "wear_leveling_count" }, + [0xb8] = {3, "end_to_end_error_count" }, + [0xc7] = {4, "crc_error_count" }, + [0xe2] = {5, "timed_workload_media_wear" }, + [0xe3] = {6, "timed_workload_host_reads" }, + [0xe4] = {7, "timed_workload_timer" }, + [0xea] = {8, "thermal_throttle_status" }, + [0xf0] = {9, "lifetime_write_amplification"}, + [0xf3] = {10, "pll_lock_loss_count" }, + [0xf4] = {11, "nand_bytes_written" }, + [0xf5] = {12, "host_bytes_written" }, + [0xf6] = {13, "system_area_life_remaining" }, + [0xf9] = {14, "firmware_update_count" }, + [0xfa] = {15, "dram_cecc_count" }, + [0xfb] = {16, "dram_uecc_count" }, + [0xfc] = {17, "xor_pass_count" }, + [0xfd] = {18, "xor_fail_count" }, + [0xfe] = {19, "xor_invoked_count" }, + [0xe5] = {20, "inflight_read_io_cmd" }, + [0xe6] = {21, "flash_error_media_count" }, + [0xf8] = {22, "nand_bytes_read" }, + [0xe7] = {23, "temp_since_born" }, + [0xe8] = {24, "power_consumption" }, + [0xaf] = {25, "temp_since_bootup" }, + [0xeb] = {26, "thermal_throttle_time" }, + }; + + for (int i = 0; i < item_count; i++, item++) { + if (item->id == 0) + continue; + + printf("%#-12" PRIx8 "%-36s%-12d", item->id, items[item->id].attr, item->norm); + switch (item->id) { + case 0xad: + printf("min: %d, max: %d, avg: %d\n", + le16_to_cpu(item->wear_level.min), + le16_to_cpu(item->wear_level.max), + le16_to_cpu(item->wear_level.avg)); + break; + case 0xe7: + printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", + K2C(le16_to_cpu(item->temp_since_born.max)), + le16_to_cpu(item->temp_since_born.max), + K2C(le16_to_cpu(item->temp_since_born.min)), + le16_to_cpu(item->temp_since_born.min), + K2C(le16_to_cpu(item->temp_since_born.curr)), + le16_to_cpu(item->temp_since_born.curr)); + break; + case 0xe8: + printf("max: %d, min: %d, curr: %d\n", + le16_to_cpu(item->power_consumption.max), + le16_to_cpu(item->power_consumption.min), + le16_to_cpu(item->power_consumption.curr)); + break; + case 0xaf: + printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", + K2C(le16_to_cpu(item->temp_since_power_on.max)), + le16_to_cpu(item->temp_since_power_on.max), + K2C(le16_to_cpu(item->temp_since_power_on.min)), + le16_to_cpu(item->temp_since_power_on.min), + K2C(le16_to_cpu(item->temp_since_power_on.curr)), + le16_to_cpu(item->temp_since_power_on.curr)); + break; + default: + printf("%" PRIu64 "\n", int48_to_long(item->raw)); + break; + } + } +} + +static void smart_log_add_v3_print(struct smart_log_add_item_10 *item, int item_count) +{ + static const struct smart_log_add_item items[0xff] = { + [0xab] = {0, "program_fail_count" }, + [0xac] = {1, "erase_fail_count" }, + [0xad] = {2, "wear_leveling_count" }, + [0xb8] = {3, "ext_e2e_err_count" }, + [0xc7] = {4, "crc_err_count" }, + [0xf4] = {5, "nand_bytes_written" }, + [0xf5] = {6, "host_bytes_written" }, + [0xd0] = {7, "reallocated_sector_count" }, + [0xd1] = {8, "uncorrectable_sector_count" }, + [0xd2] = {9, "nand_uecc_detection" }, + [0xd3] = {10, "nand_xor_correction" }, + [0xd4] = {12, "gc_count" }, // 11 is reserved + [0xd5] = {13, "dram_uecc_detection_count" }, + [0xd6] = {14, "sram_uecc_detection_count" }, + [0xd7] = {15, "internal_raid_recovery_fail_count"}, + [0xd8] = {16, "inflight_cmds" }, + [0xd9] = {17, "internal_e2e_err_count" }, + [0xda] = {19, "die_fail_count" }, // 18 is reserved + [0xdb] = {20, "wear_leveling_execution_count" }, + [0xdc] = {21, "read_disturb_count" }, + [0xdd] = {22, "data_retention_count" }, + [0xde] = {23, "capacitor_health" }, + }; + + for (int i = 0; i < item_count; i++, item++) { + if (item->id == 0) + continue; + + printf("%#-12" PRIx8 "%-36s%-12d", item->id, items[item->id].attr, item->norm); + switch (item->id) { + case 0xad: + printf("min: %d, max: %d, avg: %d\n", + le16_to_cpu(item->wear_level.min), + le16_to_cpu(item->wear_level.max), + le16_to_cpu(item->wear_level.avg)); + break; + default: + printf("%" PRIu64 "\n", int48_to_long(item->raw)); + break; + } + } +} + +static void smart_log_add_print(struct smart_log_add *log, const char *devname) +{ + uint8_t version = log->raw[511]; + + printf("Version: %u\n", version); + printf("\n"); + printf("Additional Smart Log for NVMe device: %s\n", devname); + printf("\n"); + + printf("%-12s%-36s%-12s%s\n", "Id", "Key", "Normalized", "Raw"); + + switch (version) { + case 0: + return smart_log_add_v0_print(&log->v0_raw[0], + sizeof(struct smart_log_add_v0) / sizeof(struct smart_log_add_item_12)); + case 2: + return smart_log_add_v2_print(&log->v2_raw[0], + sizeof(struct smart_log_add_v2) / sizeof(struct smart_log_add_item_12)); + case 3: + return smart_log_add_v3_print(&log->v3_raw[0], + sizeof(struct smart_log_add_v3) / sizeof(struct smart_log_add_item_10)); + + case 1: + fprintf(stderr, "Version %d: N/A\n", version); + break; + default: + fprintf(stderr, "Version %d: Not supported yet\n", version); + break; + } +} + +static int mb_get_smart_log_add(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int err = 0; + + // Get the configuration + + struct config { + bool raw_binary; + }; + + struct config cfg = {0}; + + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, "dump the whole log buffer in binary format"), + OPT_END()}; + + // Open device + + struct nvme_dev *dev = NULL; + + err = parse_and_open(&dev, argc, argv, cmd->help, opts); + if (err) + return err; + + // Get log + + struct smart_log_add log = {0}; + + err = nvme_get_log_simple(dev_fd(dev), LID_SMART_LOG_ADD, sizeof(struct smart_log_add), &log); + if (!err) { + if (!cfg.raw_binary) + smart_log_add_print(&log, dev->name); + else + d_raw((unsigned char *)&log, sizeof(struct smart_log_add)); + } else if (err > 0) { + nvme_show_status(err); + } else { + nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); + } + + // Close device + + dev_close(dev); + return err; +} + +// performance-monitor + +struct latency_stats_bucket { + char *start_threshold; + char *end_threshold; +}; + +struct __packed latency_stats { + union { + struct latency_stats_v2_0 { + uint32_t minor_version; + uint32_t major_version; + uint32_t bucket_read_data[32]; + uint32_t rsvd[32]; + uint32_t bucket_write_data[32]; + uint32_t rsvd1[32]; + uint32_t bucket_trim_data[32]; + uint32_t rsvd2[32]; + uint8_t rsvd3[248]; + } v2_0; + uint8_t raw[1024]; + }; +}; + +struct __packed high_latency_log { + union { + struct high_latency_log_v1 { + uint32_t version; + struct high_latency_log_entry { + uint64_t timestamp; // ms + uint32_t latency; + uint32_t qid; + uint32_t opcode : 8; + uint32_t fuse : 2; + uint32_t psdt : 2; + uint32_t cid : 16; + uint32_t rsvd : 4; + uint32_t nsid; + uint64_t slba; + uint32_t nlb : 16; + uint32_t dtype : 8; + uint32_t pinfo : 4; + uint32_t fua : 1; + uint32_t lr : 1; + uint32_t rsvd1 : 2; + uint8_t rsvd2[28]; + } entries[1024]; + } v1; + uint8_t raw[4 + 1024 * 64]; + }; +}; + +struct __packed performance_stats { + union { + struct performance_stats_v1 { + uint8_t version; + uint8_t rsvd[3]; + struct performance_stats_timestamp { + uint8_t timestamp[6]; + struct performance_stats_entry { + uint16_t read_iops; // K IOPS + uint16_t read_bandwidth; // MiB + uint32_t read_latency; // us + uint32_t read_latency_max; // us + uint16_t write_iops; // K IOPS + uint16_t write_bandwidth; // MiB + uint32_t write_latency; // us + uint32_t write_latency_max; // us + } entries[3600]; + } timestamps[24]; + } v1; + uint8_t raw[4 + 24 * (6 + 3600 * 24)]; + }; +}; + +static int mb_set_latency_feature(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + int err = 0; + + // Get the configuration + + struct config { + uint32_t perf_monitor; + uint32_t cmd_mask; + uint32_t read_threshold; + uint32_t write_threshold; + uint32_t de_allocate_trim_threshold; + }; + + struct config cfg = {0}; + + OPT_ARGS(opts) = { + OPT_UINT("sel-perf-log", 's', &cfg.perf_monitor, + "Select features to turn on, default: Disable\n" + " bit 0: latency statistics\n" + " bit 1: high latency log\n" + " bit 2: Performance stat"), + OPT_UINT("set-commands-mask", 'm', &cfg.cmd_mask, + "Set Enable, default: Disable\n" + " bit 0: Read commands\n" + " bit 1: high Write commands\n" + " bit 2: De-allocate/TRIM (this bit is not worked for Performance stat.)"), + OPT_UINT("set-read-threshold", 'r', &cfg.read_threshold, + "set read high latency log threshold, it's a 0-based value and unit is 10ms"), + OPT_UINT("set-write-threshold", 'w', &cfg.write_threshold, + "set write high latency log threshold, it's a 0-based value and unit is 10ms"), + OPT_UINT("set-trim-threshold", 't', &cfg.de_allocate_trim_threshold, + "set trim high latency log threshold, it's a 0-based value and unit is 10ms"), + OPT_END()}; + + // Open device + + struct nvme_dev *dev = NULL; + + err = parse_and_open(&dev, argc, argv, cmd->help, opts); + if (err) + return err; + + + // Set feature + + uint32_t result = 0; + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = FID_LATENCY_FEATURE, + .nsid = 0, + .cdw11 = 0 | cfg.perf_monitor, + .cdw12 = 0 | cfg.cmd_mask, + .cdw13 = 0 | + (cfg.read_threshold & 0xff) | + ((cfg.write_threshold & 0xff) << 8) | + ((cfg.de_allocate_trim_threshold & 0xff) << 16), + .cdw15 = 0, + .save = 0, + .uuidx = 0, + .data = NULL, + .data_len = 0, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args); + if (!err) + printf("%s have done successfully. result = %#" PRIx32 ".\n", cmd->name, result); + else if (err > 0) + nvme_show_status(err); + else + nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); + + // Close device + + dev_close(dev); + return err; +} + +static int mb_get_latency_feature(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + int err = 0; + + // Get the configuration + + OPT_ARGS(opts) = { + OPT_END()}; + + // Open device + + struct nvme_dev *dev = NULL; + + err = parse_and_open(&dev, argc, argv, cmd->help, opts); + if (err) + return err; + + // Get feature + + uint32_t result = 0; + + err = nvme_get_features_simple(dev_fd(dev), FID_LATENCY_FEATURE, 0, &result); + if (!err) { + printf("%s have done successfully. result = %#" PRIx32 ".\n", cmd->name, result); + + printf("latency statistics enable status = %d\n", (result & (0x01 << 0)) >> 0); + printf("high latency enable status = %d\n", (result & (0x01 << 1)) >> 1); + printf("performance stat enable status = %d\n", (result & (0x01 << 2)) >> 2); + + printf("Monitor Read command = %d\n", (result & (0x01 << 4)) >> 4); + printf("Monitor Write command = %d\n", (result & (0x01 << 5)) >> 5); + printf("Monitor Trim command = %d\n", (result & (0x01 << 6)) >> 6); + + printf("Threshold for Read = %dms\n", (((result & (0xff << 8)) >> 8) + 1) * 10); + printf("Threshold for Write = %dms\n", (((result & (0xff << 16)) >> 16) + 1) * 10); + printf("Threshold for Trim = %dms\n", (((result & (0xff << 24)) >> 24) + 1) * 10); + } else if (err > 0) { + nvme_show_status(err); + } else { + nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); + } + + // Close device + + dev_close(dev); + return err; +} diff --git a/plugins/memblaze/memblaze-nvme.h b/plugins/memblaze/memblaze-nvme.h index e214ca6..e25267b 100644 --- a/plugins/memblaze/memblaze-nvme.h +++ b/plugins/memblaze/memblaze-nvme.h @@ -18,6 +18,9 @@ PLUGIN(NAME("memblaze", "Memblaze vendor specific extensions", NVME_VERSION), ENTRY("lat-log", "Set Memblaze High Latency Log", mb_set_high_latency_log) ENTRY("lat-log-print", "Output Memblaze High Latency Log", mb_high_latency_log_print) ENTRY("clear-error-log", "Clear error log", memblaze_clear_error_log) + ENTRY("smart-log-add-x", "Retrieve Memblaze SMART Log, show it", mb_get_smart_log_add) + ENTRY("lat-set-feature-x", "Set Enable/Disable for Latency Monitor feature", mb_set_latency_feature) + ENTRY("lat-get-feature-x", "Get Enabled/Disabled of Latency Monitor feature", mb_get_latency_feature) ) ); diff --git a/plugins/memblaze/memblaze-utils.h b/plugins/memblaze/memblaze-utils.h index dd89189..8914f95 100644 --- a/plugins/memblaze/memblaze-utils.h +++ b/plugins/memblaze/memblaze-utils.h @@ -34,7 +34,7 @@ #define RAISIN_SI_VD_THERMAL_THROTTLE_TIME_ID 0xEB #define RAISIN_SI_VD_FLASH_MEDIA_ERROR_ID 0xED -/* Raisin Addtional smart internal ID */ +/* Raisin Additional smart internal ID */ typedef enum { /* smart attr following intel */ @@ -154,7 +154,7 @@ struct nvme_p4_smart_log /** * change 512 to 4096. * because micron's getlogpage request,the size of many commands have changed to 4k. - * request size > user malloc size,casuing parameters that are closed in momery are dirty. + * request size > user malloc size,casuing parameters that are closed in memery are dirty. */ __u8 resv[SMART_INFO_NEW_SIZE - sizeof(struct nvme_p4_smart_log_item) * NR_SMART_ITEMS]; }; diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index d6fb601..63a7a79 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -16,6 +16,7 @@ #include <limits.h> #include "linux/types.h" #include "nvme-print.h" +#include "util/cleanup.h" #define CREATE_CMD #include "micron-nvme.h" @@ -1767,10 +1768,10 @@ static void GetGenericLogs(int fd, const char *dir) struct nvme_firmware_slot fw_log; struct nvme_cmd_effects_log effects; struct nvme_persistent_event_log pevent_log; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; void *pevent_log_info = NULL; __u32 log_len = 0; int err = 0; - bool huge = false; /* get self test log */ if (!nvme_get_log_device_self_test(fd, &self_test_log)) @@ -1799,17 +1800,17 @@ static void GetGenericLogs(int fd, const char *dir) } log_len = le64_to_cpu(pevent_log.tll); - pevent_log_info = nvme_alloc(log_len, &huge); + pevent_log_info = nvme_alloc_huge(log_len, &mh); if (!pevent_log_info) { perror("could not alloc buffer for persistent event log page (ignored)!\n"); return; } + err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_READ, log_len, pevent_log_info); if (!err) WriteData((__u8 *)pevent_log_info, log_len, dir, "persistent_event_log.bin", "persistent event log"); - nvme_free(pevent_log_info, huge); } static void GetNSIDDInfo(int fd, const char *dir, int nsid) @@ -2221,7 +2222,7 @@ static int display_fw_activate_entry(int entry_count, struct fw_activation_histo else sprintf(ptr, "| pass"); - /* replace all null charecters with spaces */ + /* replace all null characters with spaces */ ptr = formatted_entry; while (index < entry_size) { if (ptr[index] == '\0') @@ -2289,7 +2290,7 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c goto out; } - /* check if we have atleast one entry to print */ + /* check if we have at least one entry to print */ struct micron_fw_activation_history_table *table = (struct micron_fw_activation_history_table *)logC2; @@ -2696,7 +2697,7 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c /* check for models that support 0xC0 log */ if (eModel != M51CX) { - printf("Unsupported drive model for vs-smart-add-log commmand\n"); + printf("Unsupported drive model for vs-smart-add-log command\n"); err = -1; goto out; } diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index e8b3fed..2193ffb 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -543,7 +543,7 @@ int show_nbft(int argc, char **argv, struct command *cmd, struct plugin *plugin) if (ret) return ret; - ret = flags = validate_output_format(format); + ret = validate_output_format(format, &flags); if (ret < 0) return ret; diff --git a/plugins/ocp/meson.build b/plugins/ocp/meson.build index 405ee51..64447ff 100644 --- a/plugins/ocp/meson.build +++ b/plugins/ocp/meson.build @@ -1,7 +1,7 @@ sources += [ 'plugins/ocp/ocp-utils.c', 'plugins/ocp/ocp-nvme.c', - 'plugins/ocp/ocp-clear-fw-update-history.c', + 'plugins/ocp/ocp-clear-features.c', 'plugins/ocp/ocp-smart-extended-log.c', 'plugins/ocp/ocp-fw-activation-history.c', ] diff --git a/plugins/ocp/ocp-clear-features.c b/plugins/ocp/ocp-clear-features.c new file mode 100644 index 0000000..0f49584 --- /dev/null +++ b/plugins/ocp/ocp-clear-features.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Authors: haro.panosyan@solidigm.com + * leonardo.da.cunha@solidigm.com + */ + +#include <unistd.h> +#include "ocp-utils.h" +#include "nvme-print.h" + +static const __u8 OCP_FID_CLEAR_FW_ACTIVATION_HISTORY = 0xC1; +static const __u8 OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS = 0xC3; + +static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 fid) +{ + __u32 result = 0; + __u32 clear = 1 << 31; + struct nvme_dev *dev; + int uuid_index = 0; + bool uuid = true; + int err; + + OPT_ARGS(opts) = { + OPT_FLAG("no-uuid", 'n', NULL, + "Skip UUID index search (UUID index not required for OCP 1.0)"), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + if (opts[0].seen) + uuid = false; + + if (uuid) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &uuid_index); + if (err || !uuid_index) { + fprintf(stderr, "ERROR: No OCP UUID index found\n"); + goto close_dev; + } + } + + struct nvme_set_features_args args = { + .result = &result, + .data = NULL, + .args_size = sizeof(args), + .fd = dev_fd(dev), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = 0, + .cdw11 = clear, + .cdw12 = 0, + .cdw13 = 0, + .cdw15 = 0, + .data_len = 0, + .save = 0, + .uuidx = uuid_index, + .fid = fid, + }; + + err = nvme_set_features(&args); + + if (err == 0) + printf("Success : %s\n", desc); + else if (err > 0) + nvme_show_status(err); + else + printf("Fail : %s\n", desc); +close_dev: + /* Redundant close() to make static code analysis happy */ + close(dev->direct.fd); + dev_close(dev); + return err; +} + +int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "OCP Clear Firmware Update History"; + + return ocp_clear_feature(argc, argv, desc, OCP_FID_CLEAR_FW_ACTIVATION_HISTORY); +} + +int ocp_clear_pcie_correctable_errors(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "OCP Clear PCIe Correctable Error Counters"; + + return ocp_clear_feature(argc, argv, desc, + OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS); +} diff --git a/plugins/ocp/ocp-clear-features.h b/plugins/ocp/ocp-clear-features.h new file mode 100644 index 0000000..99766dd --- /dev/null +++ b/plugins/ocp/ocp-clear-features.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022 Solidigm. + * + * Authors: haro.panosyan@solidigm.com + * leonardo.da.cunha@solidigm.com + */ + +int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin); + +int ocp_clear_pcie_correctable_errors(int argc, char **argv, struct command *cmd, + struct plugin *plugin); diff --git a/plugins/ocp/ocp-clear-fw-update-history.c b/plugins/ocp/ocp-clear-fw-update-history.c deleted file mode 100644 index 6b256b1..0000000 --- a/plugins/ocp/ocp-clear-fw-update-history.c +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2022 Solidigm. - * - * Authors: haro.panosyan@solidigm.com - * leonardo.da.cunha@solidigm.com - */ - -#include <unistd.h> -#include "ocp-utils.h" -#include "nvme-print.h" - -static const __u8 OCP_FID_CLEAR_FW_ACTIVATION_HISTORY = 0xC1; - -int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin) -{ - const char *desc = "OCP Clear Firmware Update History"; - - return ocp_clear_feature(argc, argv, desc, OCP_FID_CLEAR_FW_ACTIVATION_HISTORY); -} diff --git a/plugins/ocp/ocp-clear-fw-update-history.h b/plugins/ocp/ocp-clear-fw-update-history.h deleted file mode 100644 index cd0844c..0000000 --- a/plugins/ocp/ocp-clear-fw-update-history.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2022 Solidigm. - * - * Authors: haro.panosyan@solidigm.com - * leonardo.da.cunha@solidigm.com - */ - -int ocp_clear_fw_update_history(int argc, char **argv, - struct command *cmd, struct plugin *plugin); diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c index b067346..ad96c6b 100644 --- a/plugins/ocp/ocp-fw-activation-history.c +++ b/plugins/ocp/ocp-fw-activation-history.c @@ -207,16 +207,18 @@ int ocp_fw_activation_history_log(int argc, char **argv, struct command *cmd, } if (!err) { - const enum nvme_print_flags print_flag = validate_output_format(format); + enum nvme_print_flags print_flag; + + err = validate_output_format(format, &print_flag); + if (err < 0) { + fprintf(stderr, "Error: Invalid output format.\n"); + return err; + } if (print_flag == JSON) ocp_fw_activation_history_json(&fw_history); else if (print_flag == NORMAL) ocp_fw_activation_history_normal(&fw_history); - else { - fprintf(stderr, "Error: Invalid output format.\n"); - err = -EINVAL; - } } return err; diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index edf75fc..53ae0f4 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -25,7 +25,7 @@ #include "nvme-print.h" #include "ocp-smart-extended-log.h" -#include "ocp-clear-fw-update-history.h" +#include "ocp-clear-features.h" #include "ocp-fw-activation-history.h" #define CREATE_CMD @@ -117,7 +117,6 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, { char ts_buf[128]; int i, j; - int pos = 0; printf("-Latency Monitor/C3 Log Page Data-\n"); printf(" Controller : %s\n", dev->name); @@ -141,6 +140,8 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, printf(" Active Threshold D %d ms\n", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_d+1)); + printf(" Active Latency Configuration 0x%x \n", + le16_to_cpu(log_data->active_latency_config)); printf(" Active Latency Minimum Window %d ms\n", C3_MINIMUM_WINDOW_INCREMENT * le16_to_cpu(log_data->active_latency_min_window)); @@ -178,14 +179,6 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, printf(" Read Write Deallocate/Trim\n"); for (i = 0; i < C3_BUCKET_NUM; i++) { - printf(" Active Latency Mode: Bucket %d %27d %27d %27d\n", - i, - log_data->active_latency_config & (1 << pos), - log_data->active_latency_config & (1 << pos), - log_data->active_latency_config & (1 << pos)); - } - - for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Bucket Counter: Bucket %d %27d %27d %27d\n", i, le32_to_cpu(log_data->active_bucket_counter[i][READ]), @@ -196,10 +189,10 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Latency Time Stamp: Bucket %d ", i); for (j = 2; j >= 0; j--) { - if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1) { + if (le64_to_cpu(log_data->active_latency_timestamp[3-i][j]) == -1) { printf(" N/A "); } else { - convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf); + convert_ts(le64_to_cpu(log_data->active_latency_timestamp[3-i][j]), ts_buf); printf("%s ", ts_buf); } } @@ -209,9 +202,9 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n", i, - le16_to_cpu(log_data->active_measured_latency[i][READ-1]), - le16_to_cpu(log_data->active_measured_latency[i][WRITE-1]), - le16_to_cpu(log_data->active_measured_latency[i][TRIM-1])); + le16_to_cpu(log_data->active_measured_latency[3-i][READ-1]), + le16_to_cpu(log_data->active_measured_latency[3-i][WRITE-1]), + le16_to_cpu(log_data->active_measured_latency[3-i][TRIM-1])); } printf("\n"); @@ -226,10 +219,10 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Static Latency Time Stamp: Bucket %d ", i); for (j = 2; j >= 0; j--) { - if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1) { + if (le64_to_cpu(log_data->static_latency_timestamp[3-i][j]) == -1) { printf(" N/A "); } else { - convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf); + convert_ts(le64_to_cpu(log_data->static_latency_timestamp[3-i][j]), ts_buf); printf("%s ", ts_buf); } } @@ -239,9 +232,9 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n", i, - le16_to_cpu(log_data->static_measured_latency[i][READ-1]), - le16_to_cpu(log_data->static_measured_latency[i][WRITE-1]), - le16_to_cpu(log_data->static_measured_latency[i][TRIM-1])); + le16_to_cpu(log_data->static_measured_latency[3-i][READ-1]), + le16_to_cpu(log_data->static_measured_latency[3-i][WRITE-1]), + le16_to_cpu(log_data->static_measured_latency[3-i][TRIM-1])); } return 0; @@ -253,7 +246,6 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) char ts_buf[128]; char buf[128]; int i, j; - int pos = 0; char *operation[3] = {"Trim", "Write", "Read"}; root = json_create_object(); @@ -278,19 +270,8 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) json_object_add_value_uint(root, "Active Threshold D", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_d + 1)); - - for (i = 0; i < C3_BUCKET_NUM; i++) { - struct json_object *bucket; - - bucket = json_create_object(); - sprintf(buf, "Active Latency Mode: Bucket %d", i); - for (j = 2; j >= 0; j--) { - json_object_add_value_uint(bucket, operation[j], - log_data->active_latency_config & (1 << pos)); - } - json_object_add_value_object(root, buf, bucket); - } - + json_object_add_value_uint(root, "Active Latency Configuration", + le16_to_cpu(log_data->active_latency_config)); json_object_add_value_uint(root, "Active Latency Minimum Window", C3_MINIMUM_WINDOW_INCREMENT * le16_to_cpu(log_data->active_latency_min_window)); @@ -313,10 +294,10 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) bucket = json_create_object(); sprintf(buf, "Active Latency Time Stamp: Bucket %d", i); for (j = 2; j >= 0; j--) { - if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1) { + if (le64_to_cpu(log_data->active_latency_timestamp[3-i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); } else { - convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf); + convert_ts(le64_to_cpu(log_data->active_latency_timestamp[3-i][j]), ts_buf); json_object_add_value_string(bucket, operation[j], ts_buf); } } @@ -330,7 +311,7 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) sprintf(buf, "Active Measured Latency: Bucket %d", i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], - le16_to_cpu(log_data->active_measured_latency[i][j])); + le16_to_cpu(log_data->active_measured_latency[3-i][j])); } json_object_add_value_object(root, buf, bucket); } @@ -356,10 +337,10 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) bucket = json_create_object(); sprintf(buf, "Static Latency Time Stamp: Bucket %d", i); for (j = 2; j >= 0; j--) { - if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1) { + if (le64_to_cpu(log_data->static_latency_timestamp[3-i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); } else { - convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf); + convert_ts(le64_to_cpu(log_data->static_latency_timestamp[3-i][j]), ts_buf); json_object_add_value_string(bucket, operation[j], ts_buf); } } @@ -373,7 +354,7 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) sprintf(buf, "Static Measured Latency: Bucket %d", i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], - le16_to_cpu(log_data->static_measured_latency[i][j])); + le16_to_cpu(log_data->static_measured_latency[3-i][j])); } json_object_add_value_object(root, buf, bucket); } @@ -416,15 +397,15 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) static int get_c3_log_page(struct nvme_dev *dev, char *format) { struct ssd_latency_monitor_log *log_data; - int ret = 0; - int fmt = -1; + enum nvme_print_flags fmt; + int ret; __u8 *data; int i; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); - return fmt; + return ret; } data = malloc(sizeof(__u8) * C3_LATENCY_MON_LOG_BUF_LEN); @@ -481,6 +462,9 @@ static int get_c3_log_page(struct nvme_dev *dev, char *format) case JSON: ocp_print_C3_log_json(log_data); break; + default: + fprintf(stderr, "unhandled output format\n"); + } } else { fprintf(stderr, @@ -707,7 +691,7 @@ static int eol_plp_failure_mode_get(struct nvme_dev *dev, const __u32 nsid, nvme_select_to_string(sel), eol_plp_failure_mode_to_string(result)); if (sel == NVME_GET_FEATURES_SEL_SUPPORTED) - nvme_show_select_result(result); + nvme_show_select_result(fid, result); } else { nvme_show_error("Could not get feature: %#0*x.", fid ? 4 : 2, fid); } @@ -847,6 +831,31 @@ struct telemetry_initiated_log { __u8 ReasonIdentifier[128]; }; +struct telemetry_data_area_1 { + __le16 major_version; + __le16 minor_version; + __u8 reserved1[4]; + __le64 timestamp; + __u8 log_page_guid[16]; + __u8 no_of_tps_supp; + __u8 tps; + __u8 reserved2[6]; + __le16 sls; + __u8 reserved3[8]; + __le16 fw_revision; + __u8 reserved4[32]; + __le16 da1_stat_start; + __le16 da1_stat_size; + __le16 da2_stat_start; + __le16 da2_stat_size; + __u8 reserved5[32]; + __u8 event_fifo_da[16]; + __le64 event_fifo_start[16]; + __le64 event_fifo_size[16]; + __u8 reserved6[80]; + __u8 smart_health_info[512]; + __u8 smart_health_info_extended[512]; +}; static void get_serial_number(struct nvme_id_ctrl *ctrl, char *sn) { int i; @@ -913,6 +922,144 @@ static void print_telemetry_header(struct telemetry_initiated_log *logheader, printf("===============================================\n\n"); } } +static int get_telemetry_data(struct nvme_dev *dev, __u32 ns, __u8 tele_type, + __u32 data_len, void *data, __u8 nLSP, __u8 nRAE, + __u64 offset) +{ + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_get_log_page, + .nsid = ns, + .addr = (__u64)(uintptr_t) data, + .data_len = data_len, + }; + __u32 numd = (data_len >> 2) - 1; + __u16 numdu = numd >> 16; + __u16 numdl = numd & 0xffff; + cmd.cdw10 = tele_type | (nLSP & 0x0F) << 8 | (nRAE & 0x01) << 15 | (numdl & 0xFFFF) << 16; + cmd.cdw11 = numdu; + cmd.cdw12 = offset; + cmd.cdw13 = 0; + cmd.cdw14 = 0; + return nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL); +} +static void print_telemetry_data_area_1(struct telemetry_data_area_1 *da1, + int tele_type) +{ + if (da1) { + unsigned int i = 0; + if (tele_type == TELEMETRY_TYPE_HOST) + printf("============ Telemetry Host Data area 1 ============\n"); + else + printf("========= Telemetry Controller Data area 1 =========\n"); + printf("Major Version : 0x%x\n", le16_to_cpu(da1->major_version)); + printf("Minor Version : 0x%x\n", le16_to_cpu(da1->minor_version)); + for (i = 0; i < 4; i++) + printf("reserved1 : 0x%x\n", da1->reserved1[i]); + printf("Timestamp : %"PRIu64"\n", le64_to_cpu(da1->timestamp)); + for (i = 15; i >= 0; i--) + printf("%x", da1->log_page_guid[i]); + printf("Number Telemetry Profiles Supported : 0x%x\n", da1->no_of_tps_supp); + printf("Telemetry Profile Selected (TPS) : 0x%x\n", da1->tps); + for (i = 0; i < 6; i++) + printf("reserved2 : 0x%x\n", da1->reserved2[i]); + printf("Telemetry String Log Size (SLS) : 0x%x\n", le16_to_cpu(da1->sls)); + for (i = 0; i < 8; i++) + printf("reserved3 : 0x%x\n", da1->reserved3[i]); + printf("Firmware Revision : 0x%x\n", le16_to_cpu(da1->fw_revision)); + for (i = 0; i < 32; i++) + printf("reserved4 : 0x%x\n", da1->reserved4[i]); + printf("Data Area 1 Statistic Start : 0x%x\n", le16_to_cpu(da1->da1_stat_start)); + printf("Data Area 1 Statistic Size : 0x%x\n", le16_to_cpu(da1->da1_stat_size)); + printf("Data Area 2 Statistic Start : 0x%x\n", le16_to_cpu(da1->da2_stat_start)); + printf("Data Area 2 Statistic Size : 0x%x\n", le16_to_cpu(da1->da2_stat_size)); + for (i = 0; i < 32; i++) + printf("reserved5 : 0x%x\n", da1->reserved5[i]); + for (i = 0; i < 17; i++){ + printf("Event FIFO %d Data Area : 0x%x\n", i, da1->event_fifo_da[i]); + printf("Event FIFO %d Start : %"PRIu64"\n", i, le64_to_cpu(da1->event_fifo_start[i])); + printf("Event FIFO %d Size : %"PRIu64"\n", i, le64_to_cpu(da1->event_fifo_size[i])); + } + for (i = 0; i < 80; i++) + printf("reserved6 : 0x%x\n", da1->reserved6[i]); + for (i = 0; i < 512; i++){ + printf("SMART / Health Information : 0x%x\n", da1->smart_health_info[i]); + printf("SMART / Health Information Extended : 0x%x\n", da1->smart_health_info_extended[i]); + } + printf("===============================================\n\n"); + } +} +static void print_telemetry_da1_stat(__u8 *da1_stat, int tele_type, __u16 buf_size) +{ + if (da1_stat) { + unsigned int i = 0; + if (tele_type == TELEMETRY_TYPE_HOST) + printf("============ Telemetry Host Data area 1 Statistics ============\n"); + else + printf("========= Telemetry Controller Data area 1 Statistics =========\n"); + while((i + 8) < buf_size) { + printf("Statistics Identifier : 0x%x\n", (da1_stat[i] | da1_stat[i+1] << 8)); + printf("Statistics info : 0x%x\n", da1_stat[i+2]); + printf("NS info : 0x%x\n", da1_stat[i+3]); + printf("Statistic Data Size : 0x%x\n", (da1_stat[i+4] | da1_stat[i+5] << 8)); + printf("Reserved : 0x%x\n", (da1_stat[i+6] | da1_stat[i+7] << 8)); + i = 8 + ((da1_stat[i+4] | da1_stat[i+5] << 8) * 4); + } + printf("===============================================\n\n"); + } +} +static void print_telemetry_da1_fifo(__u8 *da1_fifo, int tele_type, __u16 buf_size) +{ + if (da1_fifo) { + unsigned int i = 0; + if (tele_type == TELEMETRY_TYPE_HOST) + printf("============ Telemetry Host Data area 1 FIFO ============\n"); + else + printf("========= Telemetry Controller Data area 1 FIFO =========\n"); + while((i + 4) < buf_size) { + printf("Debug Event Class Type : 0x%x\n", da1_fifo[i]); + printf("Event ID : 0x%x\n", (da1_fifo[i+1] | da1_fifo[i+2] << 8)); + printf("Event Data Size : 0x%x\n", da1_fifo[3]); + i = 4 + ((da1_fifo[3]) * 4); + } + printf("===============================================\n\n"); + } +} +static void print_telemetry_da2_stat(__u8 *da1_stat, int tele_type, __u16 buf_size) +{ + if (da1_stat) { + unsigned int i = 0; + if (tele_type == TELEMETRY_TYPE_HOST) + printf("============ Telemetry Host Data area 1 Statistics ============\n"); + else + printf("========= Telemetry Controller Data area 1 Statistics =========\n"); + while((i + 8) < buf_size) { + printf("Statistics Identifier : 0x%x\n", (da1_stat[i] | da1_stat[i+1] << 8)); + printf("Statistics info : 0x%x\n", da1_stat[i+2]); + printf("NS info : 0x%x\n", da1_stat[i+3]); + printf("Statistic Data Size : 0x%x\n", (da1_stat[i+4] | da1_stat[i+5] << 8)); + printf("Reserved : 0x%x\n", (da1_stat[i+6] | da1_stat[i+7] << 8)); + i = 8 + ((da1_stat[i+4] | da1_stat[i+5] << 8) * 4); + } + printf("===============================================\n\n"); + } +} +static void print_telemetry_da2_fifo(__u8 *da1_fifo, int tele_type, __u16 buf_size) +{ + if (da1_fifo) { + unsigned int i = 0; + if (tele_type == TELEMETRY_TYPE_HOST) + printf("============ Telemetry Host Data area 1 Statistics ============\n"); + else + printf("========= Telemetry Controller Data area 1 Statistics =========\n"); + while((i + 4) < buf_size) { + printf("Debug Event Class Type : 0x%x\n", da1_fifo[i]); + printf("Event ID : 0x%x\n", (da1_fifo[i+1] | da1_fifo[i+2] << 8)); + printf("Event Data Size : 0x%x\n", da1_fifo[3]); + i = 4 + ((da1_fifo[3]) * 4); + } + printf("===============================================\n\n"); + } +} static int extract_dump_get_log(struct nvme_dev *dev, char *featurename, char *filename, char *sn, int dumpsize, int transfersize, __u32 nsid, __u8 log_id, @@ -1000,9 +1147,12 @@ static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn, { __u32 err = 0, nsid = 0; __u8 lsp = 0, rae = 0; + unsigned int i = 0; char data[TELEMETRY_TRANSFER_SIZE] = { 0 }; + char data1[1536] = { 0 }; char *featurename = 0; struct telemetry_initiated_log *logheader = (struct telemetry_initiated_log *)data; + struct telemetry_data_area_1 *da1 = (struct telemetry_data_area_1 *)data1; __u64 offset = 0, size = 0; char dumpname[FILE_NAME_SIZE] = { 0 }; @@ -1029,6 +1179,43 @@ static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn, if (header_print) print_telemetry_header(logheader, tele_type); + err = get_telemetry_data(dev, nsid, tele_type, 1536, + (void *)data1, lsp, rae, 512); + if (err) + return err; + print_telemetry_data_area_1(da1, tele_type); + char *da1_stat = calloc((da1->da1_stat_size * 4), sizeof(char)); + err = get_telemetry_data(dev, nsid, tele_type, (da1->da1_stat_size) * 4, + (void *)da1_stat, lsp, rae, (da1->da1_stat_start) * 4); + if (err) + return err; + print_telemetry_da1_stat((void *)da1_stat, tele_type, (da1->da1_stat_size) * 4); + for (i = 0; i < 17 ; i++){ + if (da1->event_fifo_da[i] == 1){ + char *da1_fifo = calloc((da1->event_fifo_size[i]) * 4, sizeof(char)); + err = get_telemetry_data(dev, nsid, tele_type, (da1->event_fifo_size[i]) * 4, + (void *)da1_stat, lsp, rae, (da1->event_fifo_start[i]) * 4); + if (err) + return err; + print_telemetry_da1_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4); + } + } + char *da2_stat = calloc((da1->da2_stat_size * 4), sizeof(char)); + err = get_telemetry_data(dev, nsid, tele_type, (da1->da2_stat_size) * 4, + (void *)da2_stat, lsp, rae, (da1->da2_stat_start) * 4); + if (err) + return err; + print_telemetry_da2_stat((void *)da2_stat, tele_type, (da1->da2_stat_size) * 4); + for (i = 0; i < 17 ; i++){ + if (da1->event_fifo_da[i] == 2){ + char *da1_fifo = calloc((da1->event_fifo_size[i]) * 4, sizeof(char)); + err = get_telemetry_data(dev, nsid, tele_type, (da1->event_fifo_size[i]) * 4, + (void *)da1_stat, lsp, rae, (da1->event_fifo_start[i]) * 4); + if (err) + return err; + print_telemetry_da2_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4); + } + } switch (data_area) { case 1: @@ -1324,17 +1511,17 @@ static void ocp_print_c5_log_binary(struct unsupported_requirement_log *log_data static int get_c5_log_page(struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; + enum nvme_print_flags fmt; + int ret; __u8 *data; int i; struct unsupported_requirement_log *log_data; int j; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); - return fmt; + return ret; } data = (__u8 *)malloc(sizeof(__u8) * C5_UNSUPPORTED_REQS_LEN); @@ -1386,6 +1573,8 @@ static int get_c5_log_page(struct nvme_dev *dev, char *format) case BINARY: ocp_print_c5_log_binary(log_data); break; + default: + break; } } else { fprintf(stderr, "ERROR : OCP : Unable to read C3 data from buffer\n"); @@ -1548,16 +1737,16 @@ static void ocp_print_c1_log_binary(struct ocp_error_recovery_log_page *log_data static int get_c1_log_page(struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; + struct ocp_error_recovery_log_page *log_data; + enum nvme_print_flags fmt; + int ret; __u8 *data; int i, j; - struct ocp_error_recovery_log_page *log_data; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); - return fmt; + return ret; } data = (__u8 *)malloc(sizeof(__u8) * C1_ERROR_RECOVERY_LOG_BUF_LEN); @@ -1609,6 +1798,8 @@ static int get_c1_log_page(struct nvme_dev *dev, char *format) case BINARY: ocp_print_c1_log_binary(log_data); break; + default: + break; } } else { fprintf(stderr, "ERROR : OCP : Unable to read C1 data from buffer\n"); @@ -1762,16 +1953,16 @@ static void ocp_print_c4_log_binary(struct ocp_device_capabilities_log_page *log static int get_c4_log_page(struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; + struct ocp_device_capabilities_log_page *log_data; + enum nvme_print_flags fmt; + int ret; __u8 *data; int i, j; - struct ocp_device_capabilities_log_page *log_data; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); - return fmt; + return ret; } data = (__u8 *)malloc(sizeof(__u8) * C4_DEV_CAP_REQ_LEN); @@ -1823,6 +2014,8 @@ static int get_c4_log_page(struct nvme_dev *dev, char *format) case BINARY: ocp_print_c4_log_binary(log_data); break; + default: + break; } } else { fprintf(stderr, "ERROR : OCP : Unable to read C4 data from buffer\n"); @@ -1867,9 +2060,891 @@ static int ocp_device_capabilities_log(int argc, char **argv, struct command *cm /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -/// Misc +/// DSSD Power State (Feature Identifier C7h) Set Feature + +static int set_dssd_power_state(struct nvme_dev *dev, const __u32 nsid, + const __u8 fid, __u8 power_state, bool save, + bool uuid) +{ + __u32 result; + int err; + int uuid_index = 0; + + if (uuid) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &uuid_index); + if (err || !uuid_index) { + nvme_show_error("ERROR: No OCP UUID index found"); + return err; + } + } + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .cdw11 = power_state, + .cdw12 = 0, + .save = save, + .uuidx = uuid_index, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args); + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + nvme_show_perror("Define DSSD Power State"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + printf("Successfully set DSSD Power State (feature: 0xC7) to below values\n"); + printf("DSSD Power State: 0x%x\n", power_state); + printf("Save bit Value: 0x%x\n", save); + } + + return err; +} + +static int set_dssd_power_state_feature(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Define DSSD Power State (Feature Identifier C7h) Set Feature."; + const char *power_state = "DSSD Power State to set in watts"; + const char *save = "Specifies that the controller shall save the attribute"; + const __u32 nsid = 0; + const __u8 fid = 0xC7; + struct nvme_dev *dev; + int err; + + struct config { + __u8 power_state; + bool save; + }; + + struct config cfg = { + .power_state = 0, + .save = false, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("power-state", 'p', &cfg.power_state, power_state), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_FLAG("no-uuid", 'n', NULL, + "Skip UUID index search (UUID index not required for OCP 1.0)"), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + if (argconfig_parse_seen(opts, "power state")) + err = set_dssd_power_state(dev, nsid, fid, cfg.power_state, + cfg.save, + !argconfig_parse_seen(opts, "no-uuid")); + + dev_close(dev); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// plp_health_check_interval + +static int set_plp_health_check_interval(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + + const char *desc = "Define Issue Set Feature command (FID : 0xC6) PLP Health Check Interval"; + const char *plp_health_interval = "[31:16]:PLP Health Check Interval"; + const char *save = "Specifies that the controller shall save the attribute"; + const __u32 nsid = 0; + const __u8 fid = 0xc6; + struct nvme_dev *dev; + int err; + __u32 result; + int uuid_index = 0; + + struct config { + __le16 plp_health_interval; + bool save; + }; + + struct config cfg = { + .plp_health_interval = 0, + .save = false, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("plp_health_interval", 'p', &cfg.plp_health_interval, plp_health_interval), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_FLAG("no-uuid", 'n', NULL, + "Skip UUID index search (UUID index not required for OCP 1.0)"), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + + if (!argconfig_parse_seen(opts, "no-uuid")) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &uuid_index); + if (err || !uuid_index) { + printf("ERROR: No OCP UUID index found"); + return err; + } + } + + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .cdw11 = cfg.plp_health_interval << 16, + .cdw12 = 0, + .save = cfg.save, + .uuidx = uuid_index, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args); + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + nvme_show_perror("Define PLP Health Check Interval"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + printf("Successfully set the PLP Health Check Interval"); + printf("PLP Health Check Interval: 0x%x\n", cfg.plp_health_interval); + printf("Save bit Value: 0x%x\n", cfg.save); + } + return err; +} + +static int get_plp_health_check_interval(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + + const char *desc = "Define Issue Get Feature command (FID : 0xC6) PLP Health Check Interval"; + const char *sel = "[0-3,8]: current/default/saved/supported/changed"; + const __u32 nsid = 0; + const __u8 fid = 0xc6; + struct nvme_dev *dev; + __u32 result; + int err; + + struct config { + __u8 sel; + }; + + struct config cfg = { + .sel = 0, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .sel = cfg.sel, + .cdw11 = 0, + .uuidx = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_get_features(&args); + if (!err) { + printf("get-feature:0xC6 %s value: %#08x\n", nvme_select_to_string(cfg.sel), result); + + if (cfg.sel == NVME_GET_FEATURES_SEL_SUPPORTED) + nvme_show_select_result(fid, result); + } else { + nvme_show_error("Could not get feature: 0xC6"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// Telemetry String Log Format Log Page (LID : C9h) + +/* C9 Telemetry String Log Format Log Page */ +#define C9_GUID_LENGTH 16 +#define C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE 0xC9 +#define C9_TELEMETRY_STR_LOG_LEN 432 +#define C9_TELEMETRY_STR_LOG_SIST_OFST 431 + +/** + * struct telemetry_str_log_format - Telemetry String Log Format + * @log_page_version: indicates the version of the mapping this log page uses + * Shall be set to 01h. + * @reserved1: Reserved. + * @log_page_guid: Shall be set to B13A83691A8F408B9EA495940057AA44h. + * @sls: Shall be set to the number of DWORDS in the String Log. + * @reserved2: reserved. + * @sits: shall be set to the number of DWORDS in the Statistics + * Identifier String Table + * @ests: Shall be set to the number of DWORDS from byte 0 of this + * log page to the start of the Event String Table + * @estsz: shall be set to the number of DWORDS in the Event String Table + * @vu_eve_sts: Shall be set to the number of DWORDS from byte 0 of this + * log page to the start of the VU Event String Table + * @vu_eve_st_sz: shall be set to the number of DWORDS in the VU Event String Table + * @ascts: the number of DWORDS from byte 0 of this log page until the ASCII Table Starts. + * @asctsz: the number of DWORDS in the ASCII Table + * @fifo1: FIFO 0 ASCII String + * @fifo2: FIFO 1 ASCII String + * @fifo3: FIFO 2 ASCII String + * @fifo4: FIFO 3 ASCII String + * @fif05: FIFO 4 ASCII String + * @fifo6: FIFO 5 ASCII String + * @fifo7: FIFO 6 ASCII String + * @fifo8: FIFO 7 ASCII String + * @fifo9: FIFO 8 ASCII String + * @fifo10: FIFO 9 ASCII String + * @fif011: FIFO 10 ASCII String + * @fif012: FIFO 11 ASCII String + * @fifo13: FIFO 12 ASCII String + * @fif014: FIFO 13 ASCII String + * @fif015: FIFO 14 ASCII String + * @fif016: FIFO 15 ASCII String + * @reserved3: reserved + */ +struct __attribute__((__packed__)) telemetry_str_log_format { + __u8 log_page_version; + __u8 reserved1[15]; + __u8 log_page_guid[C9_GUID_LENGTH]; + __le64 sls; + __u8 reserved2[24]; + __le64 sits; + __le64 sitsz; + __le64 ests; + __le64 estsz; + __le64 vu_eve_sts; + __le64 vu_eve_st_sz; + __le64 ascts; + __le64 asctsz; + __u8 fifo1[16]; + __u8 fifo2[16]; + __u8 fifo3[16]; + __u8 fifo4[16]; + __u8 fifo5[16]; + __u8 fifo6[16]; + __u8 fifo7[16]; + __u8 fifo8[16]; + __u8 fifo9[16]; + __u8 fifo10[16]; + __u8 fifo11[16]; + __u8 fifo12[16]; + __u8 fifo13[16]; + __u8 fifo14[16]; + __u8 fifo15[16]; + __u8 fifo16[16]; + __u8 reserved3[48]; +}; + +/* + * struct statistics_id_str_table_entry - Statistics Identifier String Table Entry + * @vs_si: Shall be set the Vendor Unique Statistic Identifier number. + * @reserved1: Reserved + * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. + * @ascii_id_ofst: Shall be set to the offset from DWORD 0/Byte 0 of the Start + * of the ASCII Table to the first character of the string for + * this Statistic Identifier string.. + * @reserved2 reserved + */ +struct __attribute__((__packed__)) statistics_id_str_table_entry { + __le16 vs_si; + __u8 reserved1; + __u8 ascii_id_len; + __le64 ascii_id_ofst; + __le32 reserved2; +}; + +/* + * struct event_id_str_table_entry - Event Identifier String Table Entry + * @deb_eve_class: Shall be set the Debug Class. + * @ei: Shall be set to the Event Identifier + * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. + * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the + * ASCII table to the ASCII data for this identifier + * @reserved2 reserved + */ +struct __attribute__((__packed__)) event_id_str_table_entry { + __u8 deb_eve_class; + __le16 ei; + __u8 ascii_id_len; + __le64 ascii_id_ofst; + __le32 reserved2; +}; + +/* + * struct vu_event_id_str_table_entry - VU Event Identifier String Table Entry + * @deb_eve_class: Shall be set the Debug Class. + * @vu_ei: Shall be set to the VU Event Identifier + * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. + * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the + * ASCII table to the ASCII data for this identifier + * @reserved reserved + */ +struct __attribute__((__packed__)) vu_event_id_str_table_entry { + __u8 deb_eve_class; + __le16 vu_ei; + __u8 ascii_id_len; + __le64 ascii_id_ofst; + __le32 reserved; +}; + +/* Function declaration for Telemetry String Log Format (LID:C9h) */ +static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd, + struct plugin *plugin); -static const __u8 OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS = 0xC3; + +static int ocp_print_C9_log_normal(struct telemetry_str_log_format *log_data,__u8 *log_data_buf) +{ + //calculating the index value for array + __le64 stat_id_index = (log_data->sitsz * 4) / 16; + __le64 eve_id_index = (log_data->estsz * 4) / 16; + __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16; + __le64 ascii_table_index = (log_data->asctsz * 4); + //Calculating the offset for dynamic fields. + __le64 stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4); + __le64 event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4); + __le64 vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4); + __le64 ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4); + struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index]; + struct event_id_str_table_entry event_id_str_table_arr[eve_id_index]; + struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index]; + __u8 ascii_table_info_arr[ascii_table_index]; + int j; + + printf(" Log Page Version : 0x%x\n", log_data->log_page_version); + + printf(" Reserved : "); + for (j = 0; j < 15; j++) + printf("%d", log_data->reserved1[j]); + printf("\n"); + + printf(" Log page GUID : 0x"); + for (j = C9_GUID_LENGTH - 1; j >= 0; j--) + printf("%x", log_data->log_page_guid[j]); + printf("\n"); + + printf(" Telemetry String Log Size : 0x%lx\n", le64_to_cpu(log_data->sls)); + + printf(" Reserved : "); + for (j = 0; j < 24; j++) + printf("%d", log_data->reserved2[j]); + printf("\n"); + + printf(" Statistics Identifier String Table Start : 0x%lx\n", le64_to_cpu(log_data->sits)); + printf(" Statistics Identifier String Table Size : 0x%lx\n", le64_to_cpu(log_data->sitsz)); + printf(" Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->ests)); + printf(" Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->estsz)); + printf(" VU Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->vu_eve_sts)); + printf(" VU Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->vu_eve_st_sz)); + printf(" ASCII Table Start : 0x%lx\n", le64_to_cpu(log_data->ascts)); + printf(" ASCII Table Size : 0x%lx\n", le64_to_cpu(log_data->asctsz)); + + printf(" FIFO 1 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo1[j], log_data->fifo1[j]); + } + + printf(" FIFO 2 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo2[j], log_data->fifo2[j]); + } + + printf(" FIFO 3 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo3[j], log_data->fifo3[j]); + } + + printf(" FIFO 4 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + + printf(" %d %d %c \n", j, log_data->fifo4[j], log_data->fifo4[j]); + } + + printf(" FIFO 5 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo5[j], log_data->fifo5[j]); + } + + printf(" FIFO 6 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo6[j], log_data->fifo6[j]); + } + + printf(" FIFO 7 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo7[j], log_data->fifo7[j]); + } + + printf(" FIFO 8 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf("index value ascii_val"); + printf(" %d %d %c \n", j, log_data->fifo8[j], log_data->fifo8[j]); + } + + printf(" FIFO 9 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo9[j], log_data->fifo9[j]); + } + + printf(" FIFO 10 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo10[j], log_data->fifo10[j]); + } + + printf(" FIFO 11 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo11[j], log_data->fifo11[j]); + } + + printf(" FIFO 12 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo12[j], log_data->fifo12[j]); + } + + printf(" FIFO 13 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo13[j], log_data->fifo13[j]); + } + + printf(" FIFO 14 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo14[j], log_data->fifo14[j]); + } + + printf(" FIFO 15 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo15[j], log_data->fifo16[j]); + } + + printf(" FIFO 16 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++){ + printf(" %d %d %c \n", j, log_data->fifo16[j], log_data->fifo16[j]); + } + + printf(" Reserved : "); + for (j = 0; j < 48; j++) + printf("%d", log_data->reserved3[j]); + printf("\n"); + + memcpy(stat_id_str_table_arr, (__u8*)log_data_buf + stat_id_str_table_ofst, (log_data->sitsz * 4)); + memcpy(event_id_str_table_arr, (__u8*)log_data_buf + event_str_table_ofst, (log_data->estsz * 4)); + memcpy(vu_event_id_str_table_arr, (__u8*)log_data_buf + vu_event_str_table_ofst, (log_data->vu_eve_st_sz * 4)); + memcpy(ascii_table_info_arr, (__u8*)log_data_buf + ascii_table_ofst, (log_data->asctsz * 4)); + + printf(" Statistics Identifier String Table\n"); + for (j = 0; j < stat_id_index; j++){ + printf(" Vendor Specific Statistic Identifier : 0x%x\n",le16_to_cpu(stat_id_str_table_arr[j].vs_si)); + printf(" Reserved : 0x%d",stat_id_str_table_arr[j].reserved1); + printf(" ASCII ID Length : 0x%x\n",stat_id_str_table_arr[j].ascii_id_len); + printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst)); + printf(" Reserved : 0x%d\n",stat_id_str_table_arr[j].reserved2); + } + + printf(" Event Identifier String Table Entry\n"); + for (j = 0; j < eve_id_index; j++){ + printf(" Debug Event Class : 0x%x\n",event_id_str_table_arr[j].deb_eve_class); + printf(" Event Identifier : 0x%x\n",le16_to_cpu(event_id_str_table_arr[j].ei)); + printf(" ASCII ID Length : 0x%x\n",event_id_str_table_arr[j].ascii_id_len); + printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst)); + printf(" Reserved : 0x%d\n",event_id_str_table_arr[j].reserved2); + + } + + printf(" VU Event Identifier String Table Entry\n"); + for (j = 0; j < vu_eve_index; j++){ + printf(" Debug Event Class : 0x%x\n",vu_event_id_str_table_arr[j].deb_eve_class); + printf(" VU Event Identifier : 0x%x\n",le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei)); + printf(" ASCII ID Length : 0x%x\n",vu_event_id_str_table_arr[j].ascii_id_len); + printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst)); + printf(" Reserved : 0x%d\n",vu_event_id_str_table_arr[j].reserved); + + } + + printf(" ASCII Table\n"); + printf(" Byte Data_Byte ASCII_Character\n"); + for (j = 0; j < ascii_table_index; j++){ + printf(" %lld 0x%x %c \n",ascii_table_ofst+j,ascii_table_info_arr[j],ascii_table_info_arr[j]); + } + return 0; +} + +static int ocp_print_C9_log_json(struct telemetry_str_log_format *log_data,__u8 *log_data_buf) +{ + struct json_object *root = json_create_object(); + struct json_object *stat_table = json_create_object(); + struct json_object *eve_table = json_create_object(); + struct json_object *vu_eve_table = json_create_object(); + struct json_object *entry = json_create_object(); + char res_arr[48]; + char *res = res_arr; + char guid_buf[C9_GUID_LENGTH]; + char *guid = guid_buf; + char fifo_arr[16]; + char *fifo = fifo_arr; + //calculating the index value for array + __le64 stat_id_index = (log_data->sitsz * 4) / 16; + __le64 eve_id_index = (log_data->estsz * 4) / 16; + __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16; + __le64 ascii_table_index = (log_data->asctsz * 4); + //Calculating the offset for dynamic fields. + __le64 stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4); + __le64 event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4); + __le64 vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4); + __le64 ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4); + struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index]; + struct event_id_str_table_entry event_id_str_table_arr[eve_id_index]; + struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index]; + __u8 ascii_table_info_arr[ascii_table_index]; + char ascii_buf[ascii_table_index]; + char *ascii = ascii_buf; + int j; + + json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); + + memset((__u8 *)res, 0, 15); + for (j = 0; j < 15; j++) + res += sprintf(res, "%d", log_data->reserved1[j]); + json_object_add_value_string(root, "Reserved", res_arr); + + memset((void *)guid, 0, C9_GUID_LENGTH); + for (j = C9_GUID_LENGTH - 1; j >= 0; j--) + guid += sprintf(guid, "%02x", log_data->log_page_guid[j]); + json_object_add_value_string(root, "Log page GUID", guid_buf); + + json_object_add_value_int(root, "Telemetry String Log Size", le64_to_cpu(log_data->sls)); + + memset((__u8 *)res, 0, 24); + for (j = 0; j < 24; j++) + res += sprintf(res, "%d", log_data->reserved2[j]); + json_object_add_value_string(root, "Reserved", res_arr); + + json_object_add_value_int(root, "Statistics Identifier String Table Start", le64_to_cpu(log_data->sits)); + json_object_add_value_int(root, "Event String Table Start", le64_to_cpu(log_data->ests)); + json_object_add_value_int(root, "Event String Table Size", le64_to_cpu(log_data->estsz)); + json_object_add_value_int(root, "VU Event String Table Start", le64_to_cpu(log_data->vu_eve_sts)); + json_object_add_value_int(root, "VU Event String Table Size", le64_to_cpu(log_data->vu_eve_st_sz)); + json_object_add_value_int(root, "ASCII Table Start", le64_to_cpu(log_data->ascts)); + json_object_add_value_int(root, "ASCII Table Size", le64_to_cpu(log_data->asctsz)); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo1[j]); + json_object_add_value_string(root, "FIFO 1 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo2[j]); + json_object_add_value_string(root, "FIFO 2 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo3[j]); + json_object_add_value_string(root, "FIFO 3 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo4[j]); + json_object_add_value_string(root, "FIFO 4 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo5[j]); + json_object_add_value_string(root, "FIFO 5 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo6[j]); + json_object_add_value_string(root, "FIFO 6 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo7[j]); + json_object_add_value_string(root, "FIFO 7 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo8[j]); + json_object_add_value_string(root, "FIFO 8 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo9[j]); + json_object_add_value_string(root, "FIFO 9 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo10[j]); + json_object_add_value_string(root, "FIFO 10 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo11[j]); + json_object_add_value_string(root, "FIFO 11 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo12[j]); + json_object_add_value_string(root, "FIFO 12 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo13[j]); + json_object_add_value_string(root, "FIFO 13 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo14[j]); + json_object_add_value_string(root, "FIFO 14 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo15[j]); + json_object_add_value_string(root, "FIFO 15 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo16[j]); + json_object_add_value_string(root, "FIFO 16 ASCII String", fifo_arr); + + memset((__u8 *)res, 0, 48); + for (j = 0; j < 48; j++) + res += sprintf(res, "%d", log_data->reserved3[j]); + json_object_add_value_string(root, "Reserved", res_arr); + + memcpy(stat_id_str_table_arr, (__u8*)log_data_buf + stat_id_str_table_ofst, (log_data->sitsz * 4)); + memcpy(event_id_str_table_arr, (__u8*)log_data_buf + event_str_table_ofst, (log_data->estsz * 4)); + memcpy(vu_event_id_str_table_arr, (__u8*)log_data_buf + vu_event_str_table_ofst, (log_data->vu_eve_st_sz * 4)); + memcpy(ascii_table_info_arr, (__u8*)log_data_buf + ascii_table_ofst, (log_data->asctsz * 4)); + + for (j = 0; j < stat_id_index; j++){ + json_object_add_value_int(entry, "Vendor Specific Statistic Identifier", le16_to_cpu(stat_id_str_table_arr[j].vs_si)); + json_object_add_value_int(entry, "Reserved", le64_to_cpu(stat_id_str_table_arr[j].reserved1)); + json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(stat_id_str_table_arr[j].ascii_id_len)); + json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst)); + json_object_add_value_int(entry, "Reserved", le64_to_cpu(stat_id_str_table_arr[j].reserved2)); + json_array_add_value_object(stat_table, entry); + } + json_object_add_value_array(root, "Statistics Identifier String Table", stat_table); + + for (j = 0; j < eve_id_index; j++){ + json_object_add_value_int(entry, "Debug Event Class", le16_to_cpu(event_id_str_table_arr[j].deb_eve_class)); + json_object_add_value_int(entry, "Event Identifier", le16_to_cpu(event_id_str_table_arr[j].ei)); + json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(event_id_str_table_arr[j].ascii_id_len)); + json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst)); + json_object_add_value_int(entry, "Reserved", le64_to_cpu(event_id_str_table_arr[j].reserved2)); + json_array_add_value_object(eve_table, entry); + } + json_object_add_value_array(root, "Event Identifier String Table Entry", eve_table); + + for (j = 0; j < vu_eve_index; j++){ + json_object_add_value_int(entry, "Debug Event Class", le16_to_cpu(vu_event_id_str_table_arr[j].deb_eve_class)); + json_object_add_value_int(entry, "VU Event Identifier", le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei)); + json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_len)); + json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst)); + json_object_add_value_int(entry, "Reserved", le64_to_cpu(vu_event_id_str_table_arr[j].reserved)); + json_array_add_value_object(vu_eve_table, entry); + } + json_object_add_value_array(root, "VU Event Identifier String Table Entry", vu_eve_table); + + memset((void *)ascii, 0, ascii_table_index); + for (j = 0; j < ascii_table_index; j++) + ascii += sprintf(ascii, "%c", ascii_table_info_arr[j]); + json_object_add_value_string(root, "ASCII Table", ascii_buf); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + json_free_object(stat_table); + json_free_object(eve_table); + json_free_object(vu_eve_table); + + return 0; +} + +static void ocp_print_c9_log_binary(__u8 *log_data_buf,int total_log_page_size) +{ + return d_raw((unsigned char *)log_data_buf, total_log_page_size); +} + +static int get_c9_log_page(struct nvme_dev *dev, char *format) +{ + int ret = 0; + __u8 *header_data; + struct telemetry_str_log_format *log_data; + enum nvme_print_flags fmt; + __u8 *full_log_buf_data = NULL; + __le64 stat_id_str_table_ofst = 0; + __le64 event_str_table_ofst = 0; + __le64 vu_event_str_table_ofst = 0; + __le64 ascii_table_ofst = 0; + __le64 total_log_page_sz = 0; + + ret = validate_output_format(format, &fmt); + if (ret < 0) { + fprintf(stderr, "ERROR : OCP : invalid output format\n"); + return ret; + } + + header_data = (__u8 *)malloc(sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN); + if (!header_data) { + fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); + return -1; + } + memset(header_data, 0, sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN); + + ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE, + C9_TELEMETRY_STR_LOG_LEN, header_data); + + if (!ret) { + log_data = (struct telemetry_str_log_format *)header_data; + printf("Statistics Identifier String Table Size = %lld\n",log_data->sitsz); + printf("Event String Table Size = %lld\n",log_data->estsz); + printf("VU Event String Table Size = %lld\n",log_data->vu_eve_st_sz); + printf("ASCII Table Size = %lld\n",log_data->asctsz); + + //Calculating the offset for dynamic fields. + stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4); + event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4); + vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4); + ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4); + total_log_page_sz = stat_id_str_table_ofst + event_str_table_ofst + vu_event_str_table_ofst + ascii_table_ofst; + + printf("stat_id_str_table_ofst = %lld\n",stat_id_str_table_ofst); + printf("event_str_table_ofst = %lld\n",event_str_table_ofst); + printf("vu_event_str_table_ofst = %lld\n",vu_event_str_table_ofst); + printf("ascii_table_ofst = %lld\n",ascii_table_ofst); + printf("total_log_page_sz = %lld\n",total_log_page_sz); + + full_log_buf_data = (__u8 *)malloc(sizeof(__u8) * total_log_page_sz); + if (!full_log_buf_data) { + fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); + return -1; + } + memset(full_log_buf_data, 0, sizeof(__u8) * total_log_page_sz); + + ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE, + total_log_page_sz, full_log_buf_data); + + if (!ret) { + switch (fmt) { + case NORMAL: + ocp_print_C9_log_normal(log_data,full_log_buf_data); + break; + case JSON: + ocp_print_C9_log_json(log_data,full_log_buf_data); + break; + case BINARY: + ocp_print_c9_log_binary(full_log_buf_data,total_log_page_sz); + break; + default: + fprintf(stderr, "unhandled output format\n"); + break; + } + } else{ + fprintf(stderr, "ERROR : OCP : Unable to read C9 data from buffer\n"); + } + } else { + fprintf(stderr, "ERROR : OCP : Unable to read C9 data from buffer\n"); + } + + free(header_data); + free(full_log_buf_data); + + return ret; +} + +static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + struct nvme_dev *dev; + int ret = 0; + const char *desc = "Retrieve telemetry string log format"; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "output Format: normal|json"), + OPT_END() + }; + + ret = parse_and_open(&dev, argc, argv, desc, opts); + if (ret) + return ret; + + ret = get_c9_log_page(dev, cfg.output_format); + if (ret) + fprintf(stderr, "ERROR : OCP : Failure reading the C9 Log Page, ret = %d\n", ret); + + dev_close(dev); + + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// Misc static int clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -1883,14 +2958,10 @@ static int smart_add_log(int argc, char **argv, struct command *cmd, return ocp_smart_add_log(argc, argv, cmd, plugin); } -static int clear_pcie_corectable_error_counters(int argc, char **argv, - struct command *cmd, +static int clear_pcie_correctable_error_counters(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - const char *desc = "OCP Clear PCIe Correctable Error Counters"; - - return ocp_clear_feature(argc, argv, desc, - OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS); + return ocp_clear_pcie_correctable_errors(argc, argv, cmd, plugin); } static int fw_activation_history_log(int argc, char **argv, struct command *cmd, diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h index 74dd0ef..95539b0 100644 --- a/plugins/ocp/ocp-nvme.h +++ b/plugins/ocp/ocp-nvme.h @@ -21,11 +21,15 @@ PLUGIN(NAME("ocp", "OCP cloud SSD extensions", NVME_VERSION), ENTRY("internal-log", "Retrieve and save internal device telemetry log", ocp_telemetry_log) ENTRY("clear-fw-activate-history", "Clear firmware update history log", clear_fw_update_history) ENTRY("eol-plp-failure-mode", "Define EOL or PLP circuitry failure mode.", eol_plp_failure_mode) - ENTRY("clear-pcie-correctable-error-counters", "Clear PCIe correctable error counters", clear_pcie_corectable_error_counters) - ENTRY("vs-fw-activate-history", "Get firmware activation history log", fw_activation_history_log) + ENTRY("clear-pcie-correctable-errors", "Clear PCIe correctable error counters", clear_pcie_correctable_error_counters) + ENTRY("fw-activate-history", "Get firmware activation history log", fw_activation_history_log) ENTRY("unsupported-reqs-log", "Get Unsupported Requirements Log Page", ocp_unsupported_requirements_log) ENTRY("error-recovery-log", "Retrieve Error Recovery Log Page", ocp_error_recovery_log) ENTRY("device-capability-log", "Get Device capabilities Requirements Log Page", ocp_device_capabilities_log) + ENTRY("set-dssd-power-state-feature", "Get Device capabilities Requirements Log Page", set_dssd_power_state_feature) + ENTRY("set-plp-health-check-interval", "Set PLP Health Check Interval", set_plp_health_check_interval) + ENTRY("get-plp-health-check-interval", "Get PLP Health Check Interval", get_plp_health_check_interval) + ENTRY("telemetry-string-log", "Retrieve Telemetry string Log Page", ocp_telemetry_str_log_format) ) ); diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index c989d34..0d8ba81 100644 --- a/plugins/ocp/ocp-smart-extended-log.c +++ b/plugins/ocp/ocp-smart-extended-log.c @@ -252,15 +252,15 @@ static void ocp_print_C0_log_json(void *data) static int get_c0_log_page(int fd, char *format) { + enum nvme_print_flags fmt; __u8 *data; int i; - int ret = 0; - int fmt = -1; + int ret; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); - return fmt; + return ret; } data = malloc(sizeof(__u8) * C0_SMART_CLOUD_ATTR_LEN); @@ -307,6 +307,8 @@ static int get_c0_log_page(int fd, char *format) case JSON: ocp_print_C0_log_json(data); break; + default: + break; } } else { fprintf(stderr, "ERROR : OCP : Unable to read C0 data from buffer\n"); diff --git a/plugins/ocp/ocp-utils.c b/plugins/ocp/ocp-utils.c index a37a58c..1257b30 100644 --- a/plugins/ocp/ocp-utils.c +++ b/plugins/ocp/ocp-utils.c @@ -30,66 +30,3 @@ int ocp_get_uuid_index(struct nvme_dev *dev, int *index) } return err; } - -int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 fid) -{ - __u32 result = 0; - __u32 clear = 1 << 31; - struct nvme_dev *dev; - int uuid_index = 0; - bool uuid = true; - int err; - - OPT_ARGS(opts) = { - OPT_FLAG("no-uuid", 'n', NULL, - "Skip UUID index search (UUID index not required for OCP 1.0)"), - OPT_END() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) - return err; - - if (opts[0].seen) - uuid = false; - - if (uuid) { - /* OCP 2.0 requires UUID index support */ - err = ocp_get_uuid_index(dev, &uuid_index); - if (err || !uuid_index) { - fprintf(stderr, "ERROR: No OCP UUID index found\n"); - goto close_dev; - } - } - - struct nvme_set_features_args args = { - .result = &result, - .data = NULL, - .args_size = sizeof(args), - .fd = dev_fd(dev), - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .nsid = 0, - .cdw11 = clear, - .cdw12 = 0, - .cdw13 = 0, - .cdw15 = 0, - .data_len = 0, - .save = 0, - .uuidx = uuid_index, - .fid = fid, - }; - - err = nvme_set_features(&args); - - if (err == 0) - printf("Success : %s\n", desc); - else if (err > 0) - nvme_show_status(err); - else - printf("Fail : %s\n", desc); -close_dev: - /* Redundant close() to make static code analysis happy */ - close(dev->direct.fd); - dev_close(dev); - return err; -} diff --git a/plugins/ocp/ocp-utils.h b/plugins/ocp/ocp-utils.h index a962169..d02bea9 100644 --- a/plugins/ocp/ocp-utils.h +++ b/plugins/ocp/ocp-utils.h @@ -10,11 +10,9 @@ /** * ocp_get_uuid_index() - Get OCP UUID index * @dev: nvme device - * @index: integer ponter to here to save the index + * @index: integer pointer to here to save the index * @result: The command completion result from CQE dword0 * * Return: Zero if nvme device has UUID list log page, or result of get uuid list otherwise. */ int ocp_get_uuid_index(struct nvme_dev *dev, int *index); - -int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 fid); diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index 01867c7..f752d5d 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -21,6 +21,7 @@ #include "linux/types.h" #include "nvme-wrap.h" #include "nvme-print.h" +#include "util/cleanup.h" #define CREATE_CMD #include "sfx-nvme.h" @@ -881,7 +882,7 @@ static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink) if (trg_in_4k < provisoned_cap_4k || trg_in_4k > ((__u64)provisoned_cap_4k * 4)) { fprintf(stderr, - "WARNING: Only support 1.0~4.0 x provisoned capacity!\n"); + "WARNING: Only support 1.0~4.0 x provisioned capacity!\n"); if (trg_in_4k < provisoned_cap_4k) fprintf(stderr, "WARNING: The target capacity is less than 1.0 x provisioned capacity!\n"); @@ -924,7 +925,7 @@ static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink) * * @param str, prompt string * - * @return 0, cancled; 1 confirmed + * @return 0, canceled; 1 confirmed */ static int sfx_confirm_change(const char *str) { @@ -936,7 +937,7 @@ static int sfx_confirm_change(const char *str) fprintf(stderr, "Confirm Y/y, Others cancel:\n"); confirm = (unsigned char)fgetc(stdin); if (confirm != 'y' && confirm != 'Y') { - fprintf(stderr, "Cancled.\n"); + fprintf(stderr, "Canceled.\n"); return 0; } fprintf(stderr, "Sending operation ...\n"); @@ -1349,12 +1350,12 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor { struct nvme_persistent_event_log *pevent; void *pevent_log_info; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; __u8 lsp_base; __u32 offset = 0; __u32 length = 0; __u32 log_len; __u32 single_len; - bool huge; int err = 0; FILE *fd = NULL; struct nvme_get_log_args args = { @@ -1410,7 +1411,7 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor if (log_len % 4) log_len = (log_len / 4 + 1) * 4; - pevent_log_info = nvme_alloc(single_len, &huge); + pevent_log_info = nvme_alloc_huge(single_len, &mh); if (!pevent_log_info) { err = -ENOMEM; goto free_pevent; @@ -1420,7 +1421,7 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor if (!fd) { fprintf(stderr, "Failed to open %s file to write\n", file); err = ENOENT; - goto free; + goto free_pevent; } args.lsp = lsp_base + NVME_PEVENT_LOG_READ; @@ -1453,8 +1454,8 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor printf("\nDump-evtlog: Success\n"); if (parse) { - nvme_free(pevent_log_info, huge); - pevent_log_info = nvme_alloc(log_len, &huge); + nvme_free_huge(&mh); + pevent_log_info = nvme_alloc_huge(log_len, &mh); if (!pevent_log_info) { fprintf(stderr, "Failed to alloc enough memory 0x%x to parse evtlog\n", log_len); err = -ENOMEM; @@ -1466,7 +1467,7 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor if (!fd) { fprintf(stderr, "Failed to open %s file to read\n", file); err = ENOENT; - goto free; + goto free_pevent; } if (fread(pevent_log_info, 1, log_len, fd) != log_len) { fprintf(stderr, "Failed to read evtlog to buffer\n"); @@ -1478,8 +1479,6 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor close_fd: fclose(fd); -free: - nvme_free(pevent_log_info, huge); free_pevent: free(pevent); ret: diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 0f4f59d..887e5bc 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -926,7 +926,7 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi /** * Here we should identify if the drive is a Panthor or Jaguar. * Here we need to extract the model no from ctrl-id abd use it - * to deternine drive family. + * to determine drive family. */ err = nvme_identify_ctrl(dev_fd(dev), &ctrl); @@ -1299,7 +1299,7 @@ static void print_stx_vs_fw_activate_history(stx_fw_activ_history_log_page fwAct char buf[80]; if (fwActivHis.numValidFwActHisEnt > 0) { - printf("\n\nSeagate FW Activation Histry :\n"); + printf("\n\nSeagate FW Activation History :\n"); printf("%-9s %-21s %-7s %-13s %-9s %-5s %-15s %-9s\n", "Counter ", " Timestamp ", " PCC ", "Previous FW ", "New FW ", "Slot", "Commit Action", "Result"); for (i = 0; i < fwActivHis.numValidFwActHisEnt; i++) { @@ -1406,7 +1406,7 @@ static int stx_vs_fw_activate_history(int argc, char **argv, struct command *cmd } if (strcmp(cfg.output_format, "json")) - printf("Seagate FW Activation Histry Information :\n"); + printf("Seagate FW Activation History Information :\n"); err = nvme_get_log_simple(dev_fd(dev), 0xC2, sizeof(fwActivHis), &fwActivHis); if (!err) { diff --git a/plugins/solidigm/meson.build b/plugins/solidigm/meson.build index 84495a1..052afa1 100644 --- a/plugins/solidigm/meson.build +++ b/plugins/solidigm/meson.build @@ -8,6 +8,9 @@ sources += [ 'plugins/solidigm/solidigm-telemetry.c', 'plugins/solidigm/solidigm-internal-logs.c', 'plugins/solidigm/solidigm-market-log.c', + 'plugins/solidigm/solidigm-temp-stats.c', + 'plugins/solidigm/solidigm-get-drive-info.c', + 'plugins/solidigm/solidigm-ocp-version.c', ] subdir('solidigm-telemetry') diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c index b26d754..a37e9c5 100644 --- a/plugins/solidigm/solidigm-garbage-collection.c +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -68,6 +68,7 @@ static void vu_gc_log_show(struct garbage_control_collection_log *payload, const int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Get and parse Solidigm vendor specific garbage collection event log."; + enum nvme_print_flags flags; struct nvme_dev *dev; int err; __u8 uuid_index; @@ -89,9 +90,8 @@ int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *c if (err) return err; - enum nvme_print_flags flags = validate_output_format(cfg.output_format); - - if (flags == -EINVAL) { + err = validate_output_format(cfg.output_format, &flags); + if (err) { fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format); dev_close(dev); return -EINVAL; diff --git a/plugins/solidigm/solidigm-get-drive-info.c b/plugins/solidigm/solidigm-get-drive-info.c new file mode 100644 index 0000000..21f59bb --- /dev/null +++ b/plugins/solidigm/solidigm-get-drive-info.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Solidigm. + * + * Authors: leonardo.da.cunha@solidigm.com + */ + +#include <errno.h> +#include "nvme-print.h" +#include "nvme-wrap.h" +#include "common.h" + +int sldgm_get_drive_info(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + const char *desc = "Get drive HW information"; + const char *FTL_unit_size_str = "FTL_unit_size"; + char *output_format = "normal"; + enum nvme_print_flags flags; + nvme_root_t r; + nvme_ctrl_t c; + nvme_ns_t n; + struct nvme_id_ns ns = { 0 }; + __u8 flbaf_inUse; + __u16 lba_size; + __u16 ftl_unit_size; + int err; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &output_format, "normal|json"), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = validate_output_format(output_format, &flags); + if ((err < 0) || !(flags == NORMAL || flags == JSON)) { + nvme_show_error("Invalid output format"); + return err; + } + + r = nvme_scan(NULL); + c = nvme_scan_ctrl(r, dev->name); + n = c ? nvme_ctrl_first_ns(c) : nvme_scan_namespace(dev->name); + if (!n) { + nvme_show_error("solidigm-vs-drive-info: drive missing namespace"); + return -EINVAL; + } + + err = nvme_ns_identify(n, &ns); + if (err) { + nvme_show_error("identify namespace: %s", nvme_strerror(errno)); + return err; + } + + if (!(ns.nsfeat & 0x10)) { + nvme_show_error("solidigm-vs-drive-info: performance options not available"); + return -EINVAL; + } + + nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &flbaf_inUse); + lba_size = 1 << ns.lbaf[flbaf_inUse].ds; + ftl_unit_size = (le16_to_cpu(ns.npwg) + 1) * lba_size / 1024; + + if (flags == JSON) { + struct json_object *root = json_create_object(); + + json_object_add_value_int(root, FTL_unit_size_str, ftl_unit_size); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + printf("%s: %d\n", FTL_unit_size_str, ftl_unit_size); + } + + return err; +} diff --git a/plugins/solidigm/solidigm-get-drive-info.h b/plugins/solidigm/solidigm-get-drive-info.h new file mode 100644 index 0000000..ffc1bd2 --- /dev/null +++ b/plugins/solidigm/solidigm-get-drive-info.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int sldgm_get_drive_info(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index 4730443..236652a 100644 --- a/plugins/solidigm/solidigm-internal-logs.c +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -19,6 +19,7 @@ #include "libnvme.h" #include "plugin.h" #include "nvme-print.h" +#include "solidigm-util.h" #define DWORD_SIZE 4 @@ -427,7 +428,8 @@ static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetr int err = 0, output; __u8 *buffer = NULL; size_t bytes_remaining = 0; - int data_area = NVME_TELEMETRY_DA_3; + enum nvme_telemetry_da da; + size_t max_data_tx; char file_path[PATH_MAX]; char *log_name; @@ -444,6 +446,12 @@ static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetr default: return -EINVAL; } + err = nvme_get_telemetry_max(dev_fd(dev), &da, &max_data_tx); + if (err) + return err; + + if (max_data_tx > DRIVER_MAX_TX_256K) + max_data_tx = DRIVER_MAX_TX_256K; sprintf(file_path, "%s_%s.bin", cfg.file_prefix, log_name); output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); @@ -452,16 +460,16 @@ static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetr switch (ttype) { case HOSTGENNEW: - err = nvme_get_new_host_telemetry(dev_fd(dev), &log, - data_area, &log_size); + err = nvme_get_telemetry_log(dev_fd(dev), true, false, false, max_data_tx, da, + &log, &log_size); break; case HOSTGENOLD: - err = nvme_get_host_telemetry(dev_fd(dev), &log, - data_area, &log_size); + err = nvme_get_telemetry_log(dev_fd(dev), false, false, false, max_data_tx, da, + &log, &log_size); break; case CONTROLLER: - err = nvme_get_ctrl_telemetry(dev_fd(dev), true, &log, - data_area, &log_size); + err = nvme_get_telemetry_log(dev_fd(dev), false, true, true, max_data_tx, da, &log, + &log_size); break; } @@ -546,42 +554,42 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *command, if (err == 0) log_count++; else if (err < 0) - perror("Assert log"); + perror("Error retrieving Assert log"); } if (all || !strcmp(cfg.type, "EVENT")) { err = dump_event_logs(dev, cfg); if (err == 0) log_count++; else if (err < 0) - perror("Eventt log"); + perror("Error retrieving Event log"); } if (all || !strcmp(cfg.type, "NLOG")) { err = dump_nlogs(dev, cfg, -1); if (err == 0) log_count++; else if (err < 0) - perror("Nlog"); + perror("Error retrieving Nlog"); } if (all || !strcmp(cfg.type, "CONTROLLERINITTELEMETRY")) { err = dump_telemetry(dev, cfg, CONTROLLER); if (err == 0) log_count++; else if (err < 0) - perror("Telemetry Controller Initated"); + perror("Error retrieving Telemetry Controller Initiated"); } if (all || !strcmp(cfg.type, "HOSTINITTELEMETRYNOGEN")) { err = dump_telemetry(dev, cfg, HOSTGENOLD); if (err == 0) log_count++; else if (err < 0) - perror("Previously existing Telemetry Host Initated"); + perror("Error retrieving previously existing Telemetry Host Initiated"); } if (all || !strcmp(cfg.type, "HOSTINITTELEMETRY")) { err = dump_telemetry(dev, cfg, HOSTGENNEW); if (err == 0) log_count++; else if (err < 0) - perror("Telemetry Host Initated"); + perror("Error retrieving Telemetry Host Initiated"); } if (log_count == 0) { diff --git a/plugins/solidigm/solidigm-latency-tracking.c b/plugins/solidigm/solidigm-latency-tracking.c index 481a831..66f3c56 100644 --- a/plugins/solidigm/solidigm-latency-tracking.c +++ b/plugins/solidigm/solidigm-latency-tracking.c @@ -411,8 +411,8 @@ int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *cmd lt.fd = dev_fd(dev); - lt.print_flags = validate_output_format(lt.cfg.output_format); - if (lt.print_flags == -EINVAL) { + err = validate_output_format(lt.cfg.output_format, <.print_flags); + if (err < 0) { fprintf(stderr, "Invalid output format '%s'\n", lt.cfg.output_format); dev_close(dev); return -EINVAL; diff --git a/plugins/solidigm/solidigm-log-page-dir.c b/plugins/solidigm/solidigm-log-page-dir.c index 111a433..bf272f8 100644 --- a/plugins/solidigm/solidigm-log-page-dir.c +++ b/plugins/solidigm/solidigm-log-page-dir.c @@ -100,38 +100,13 @@ static struct lid_dir *get_standard_lids(struct nvme_supported_log_pages *suppor static struct lid_dir standard_dir = { 0 }; init_lid_dir(&standard_dir); - standard_dir.lid[0x00].str = "Supported Log Pages"; - standard_dir.lid[0x01].str = "Error Information"; - standard_dir.lid[0x02].str = "SMART / Health Information"; - standard_dir.lid[0x03].str = "Firmware Slot Information"; - standard_dir.lid[0x04].str = "Changed Namespace List"; - standard_dir.lid[0x05].str = "Commands Supported and Effects"; - standard_dir.lid[0x06].str = "Device Self Test"; - standard_dir.lid[0x07].str = "Telemetry Host-Initiated"; - standard_dir.lid[0x08].str = "Telemetry Controller-Initiated"; - standard_dir.lid[0x09].str = "Endurance Group Information"; - standard_dir.lid[0x0A].str = "Predictable Latency Per NVM Set"; - standard_dir.lid[0x0B].str = "Predictable Latency Event Aggregate"; - standard_dir.lid[0x0C].str = "Asymmetric Namespace Access"; - standard_dir.lid[0x0D].str = "Persistent Event Log"; - standard_dir.lid[0x0E].str = "Predictable Latency Event Aggregate"; - standard_dir.lid[0x0F].str = "Endurance Group Event Aggregate"; - standard_dir.lid[0x10].str = "Media Unit Status"; - standard_dir.lid[0x11].str = "Supported Capacity Configuration List"; - standard_dir.lid[0x12].str = "Feature Identifiers Supported and Effects"; - standard_dir.lid[0x13].str = "NVMe-MI Commands Supported and Effects"; - standard_dir.lid[0x14].str = "Command and Feature lockdown"; - standard_dir.lid[0x15].str = "Boot Partition"; - standard_dir.lid[0x16].str = "Rotational Media Information"; - standard_dir.lid[0x70].str = "Discovery"; - standard_dir.lid[0x80].str = "Reservation Notification"; - standard_dir.lid[0x81].str = "Sanitize Status"; for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { if (!supported->lid_support[lid] || lid >= MIN_VENDOR_LID) continue; standard_dir.lid[lid].supported = true; + standard_dir.lid[lid].str = nvme_log_to_string(lid); } return &standard_dir; @@ -158,6 +133,14 @@ static struct lid_dir *get_solidigm_lids(struct nvme_supported_log_pages *suppor solidigm_dir.lid[0xC4].str = "Endurance Manager Statistics"; solidigm_dir.lid[0xC5].str = "Temperature Statistics"; solidigm_dir.lid[0xCA].str = "SMART Attributes"; + solidigm_dir.lid[0xCB].str = "VU NVMe IO Queue Metrics Log Page"; + solidigm_dir.lid[0xDD].str = "VU Marketing Description Log Page"; + solidigm_dir.lid[0xEF].str = "Performance Rating and LBA Access Histogram"; + solidigm_dir.lid[0xF2].str = "Get Power Usage Log Page"; + solidigm_dir.lid[0xF6].str = "Vt Histo Get Log Page"; + solidigm_dir.lid[0xF9].str = "Workload Tracker Get Log Page"; + solidigm_dir.lid[0xFD].str = "Garbage Control Collection Log Page"; + solidigm_dir.lid[0xFE].str = "Latency Outlier Log Page"; update_vendor_lid_supported(supported, &solidigm_dir); @@ -281,15 +264,18 @@ int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *c } if (!err) { - const enum nvme_print_flags print_flag = validate_output_format(format); + enum nvme_print_flags print_flag; + + err = validate_output_format(format, &print_flag); + if (err < 0) { + fprintf(stderr, "Error: Invalid output format specified: %s.\n", format); + return err; + } if (print_flag == NORMAL) { supported_log_pages_normal(lid_dirs); } else if (print_flag == JSON) { supported_log_pages_json(lid_dirs); - } else { - fprintf(stderr, "Error: Invalid output format specified: %s.\n", format); - err = -EINVAL; } } diff --git a/plugins/solidigm/solidigm-nvme.c b/plugins/solidigm/solidigm-nvme.c index b0db1ea..3fb86f5 100644 --- a/plugins/solidigm/solidigm-nvme.c +++ b/plugins/solidigm/solidigm-nvme.c @@ -18,8 +18,11 @@ #include "solidigm-telemetry.h" #include "solidigm-log-page-dir.h" #include "solidigm-market-log.h" +#include "solidigm-temp-stats.h" +#include "solidigm-get-drive-info.h" +#include "solidigm-ocp-version.h" -#include "plugins/ocp/ocp-clear-fw-update-history.h" +#include "plugins/ocp/ocp-clear-features.h" #include "plugins/ocp/ocp-smart-extended-log.h" #include "plugins/ocp/ocp-fw-activation-history.h" @@ -59,6 +62,12 @@ static int clear_fw_update_history(int argc, char **argv, struct command *cmd, return ocp_clear_fw_update_history(argc, argv, cmd, plugin); } +static int clear_pcie_correctable_error_counters(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return ocp_clear_pcie_correctable_errors(argc, argv, cmd, plugin); +} + static int smart_cloud(int argc, char **argv, struct command *cmd, struct plugin *plugin) { @@ -82,3 +91,19 @@ static int get_market_log(int argc, char **argv, struct command *cmd, { return sldgm_get_market_log(argc, argv, cmd, plugin); } + +static int get_temp_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return sldgm_get_temp_stats_log(argc, argv, cmd, plugin); +} + +static int get_drive_info(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return sldgm_get_drive_info(argc, argv, cmd, plugin); +} + +static int get_cloud_SSDplugin_version(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return sldgm_ocp_version(argc, argv, cmd, plugin); +} diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h index 69b63e5..a984a38 100644 --- a/plugins/solidigm/solidigm-nvme.h +++ b/plugins/solidigm/solidigm-nvme.h @@ -13,7 +13,7 @@ #include "cmd.h" -#define SOLIDIGM_PLUGIN_VERSION "0.14" +#define SOLIDIGM_PLUGIN_VERSION "1.0" PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), COMMAND_LIST( @@ -25,9 +25,13 @@ PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_V ENTRY("market-log", "Retrieve Market Log", get_market_log) ENTRY("latency-tracking-log", "Enable/Retrieve Latency tracking Log", get_latency_tracking_log) ENTRY("parse-telemetry-log", "Parse Telemetry Log binary", get_telemetry_log) + ENTRY("clear-pcie-correctable-errors ", "Clear PCIe Correctable Error Counters (redirects to ocp plug-in)", clear_pcie_correctable_error_counters) ENTRY("clear-fw-activate-history", "Clear firmware update history log (redirects to ocp plug-in)", clear_fw_update_history) ENTRY("vs-fw-activate-history", "Get firmware activation history log (redirects to ocp plug-in)", fw_activation_history) ENTRY("log-page-directory", "Retrieve log page directory", get_log_page_directory_log) + ENTRY("temp-stats", "Retrieve Temperature Statistics log", get_temp_stats_log) + ENTRY("vs-drive-info", "Retrieve drive information", get_drive_info) + ENTRY("cloud-SSDplugin-version", "Prints plug-in OCP version", get_cloud_SSDplugin_version) ) ); diff --git a/plugins/solidigm/solidigm-ocp-version.c b/plugins/solidigm/solidigm-ocp-version.c new file mode 100644 index 0000000..4048cc1 --- /dev/null +++ b/plugins/solidigm/solidigm-ocp-version.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <stdio.h> +#include "nvme.h" + +int sldgm_ocp_version(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Prints OCP extensions version of Solidigm plugin"; + + OPT_ARGS(opts) = { + OPT_END() + }; + + int err = argconfig_parse(argc, argv, desc, opts); + + if (!err) + printf("1.0\n"); + + return err; +} diff --git a/plugins/solidigm/solidigm-ocp-version.h b/plugins/solidigm/solidigm-ocp-version.h new file mode 100644 index 0000000..d79452c --- /dev/null +++ b/plugins/solidigm/solidigm-ocp-version.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int sldgm_ocp_version(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index e3d468a..62245fa 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -69,15 +69,17 @@ static char *id_to_name(__u8 id) case 0xE2: return "media_wear_percentage"; case 0xE3: - return "host_reads"; + return "timed_work_load_host_reads"; case 0xE4: - return "timed_work_load"; + return "timed_work_load_timer"; case 0xE5: return "read_commands_in_flight_counter"; case 0xE6: return "write_commands_in_flight_counter"; case 0xEA: return "thermal_throttle_status"; + case 0xEE: + return "re_sku_count"; case 0xF0: return "retry_buffer_overflow_counter"; case 0xF3: @@ -220,11 +222,11 @@ int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd if (err) return err; - flags = validate_output_format(cfg.output_format); - if (flags == -EINVAL) { + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) { fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format); dev_close(dev); - return flags; + return err; } uuid_index = solidigm_get_vu_uuid_index(dev); diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 472284a..2bebccc 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -22,6 +22,7 @@ #include "solidigm-telemetry/header.h" #include "solidigm-telemetry/config.h" #include "solidigm-telemetry/data-area.h" +#include "solidigm-util.h" static int read_file2buffer(char *file_name, char **buffer, size_t *length) { @@ -71,7 +72,7 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc struct config cfg = { .host_gen = 1, .ctrl_init = false, - .data_area = 3, + .data_area = -1, .cfg_file = NULL, .is_input_file = false, }; @@ -90,6 +91,10 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc if (err) goto ret; + /* When not selected on the command line, get minimum data area required */ + if (cfg.data_area == -1) + cfg.data_area = cfg.cfg_file ? 3 : 1; + if (cfg.is_input_file) { if (optind >= argc) { err = errno = EINVAL; @@ -138,19 +143,23 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc } if (!cfg.is_input_file) { - if (cfg.ctrl_init) - err = nvme_get_ctrl_telemetry(dev_fd(dev), true, - &tl.log, cfg.data_area, - &tl.log_size); - else if (cfg.host_gen) - err = nvme_get_new_host_telemetry(dev_fd(dev), &tl.log, - cfg.data_area, - &tl.log_size); - else - err = nvme_get_host_telemetry(dev_fd(dev), &tl.log, - cfg.data_area, - &tl.log_size); + size_t max_data_tx; + + err = nvme_get_telemetry_max(dev_fd(dev), NULL, &max_data_tx); + if (err < 0) { + SOLIDIGM_LOG_WARNING("identify_ctrl: %s", + nvme_strerror(errno)); + goto close_fd; + } else if (err > 0) { + nvme_show_status(err); + SOLIDIGM_LOG_WARNING("Failed to acquire identify ctrl %d!", err); + goto close_fd; + } + if (max_data_tx > DRIVER_MAX_TX_256K) + max_data_tx = DRIVER_MAX_TX_256K; + err = nvme_get_telemetry_log(dev_fd(dev), cfg.host_gen, cfg.ctrl_init, true, + max_data_tx, cfg.data_area, &tl.log, &tl.log_size); if (err < 0) { SOLIDIGM_LOG_WARNING("get-telemetry-log: %s", nvme_strerror(errno)); diff --git a/plugins/solidigm/solidigm-telemetry/config.c b/plugins/solidigm/solidigm-telemetry/config.c index cc2a8bb..eceeede 100644 --- a/plugins/solidigm/solidigm-telemetry/config.c +++ b/plugins/solidigm/solidigm-telemetry/config.c @@ -9,7 +9,7 @@ #include <string.h> #include "config.h" -// max 16 bit unsigned integer nummber 65535 +// max 16 bit unsigned integer number 65535 #define MAX_16BIT_NUM_AS_STRING_SIZE 6 #define OBJ_NAME_PREFIX "UID_" diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c index 0cfa56c..14d612b 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.c +++ b/plugins/solidigm/solidigm-telemetry/data-area.c @@ -57,7 +57,7 @@ static bool telemetry_log_get_value(const struct telemetry_log *tl, char err_msg[MAX_WARNING_SIZE]; snprintf(err_msg, MAX_WARNING_SIZE, - "Value crossing 64 bit, byte aligned bounday, not supported. size_bit=%u, offset_bit_from_byte=%u.", + "Value crossing 64 bit, byte aligned boundary, not supported. size_bit=%u, offset_bit_from_byte=%u.", size_bit, offset_bit_from_byte); *val_obj = json_object_new_string(err_msg); diff --git a/plugins/solidigm/solidigm-telemetry/nlog.c b/plugins/solidigm/solidigm-telemetry/nlog.c index 43b8918..926772b 100644 --- a/plugins/solidigm/solidigm-telemetry/nlog.c +++ b/plugins/solidigm/solidigm-telemetry/nlog.c @@ -8,15 +8,16 @@ #include "nlog.h" #include "config.h" #include <string.h> -#include <math.h> #include <stdio.h> +#include "ccan/ilog/ilog.h" + #define LOG_ENTRY_HEADER_SIZE 1 #define LOG_ENTRY_TIMESTAMP_SIZE 2 #define LOG_ENTRY_NUM_ARGS_MAX 8 #define LOG_ENTRY_MAX_SIZE (LOG_ENTRY_HEADER_SIZE + LOG_ENTRY_TIMESTAMP_SIZE + \ LOG_ENTRY_NUM_ARGS_MAX) -#define NUM_ARGS_MASK ((1 << ((int)log2(LOG_ENTRY_NUM_ARGS_MAX)+1)) - 1) +#define NUM_ARGS_MASK ((1 << ((int)STATIC_ILOG_32(LOG_ENTRY_NUM_ARGS_MAX))) - 1) #define MAX_HEADER_MISMATCH_TRACK 10 static int formats_find(struct json_object *formats, uint32_t val, struct json_object **format) diff --git a/plugins/solidigm/solidigm-temp-stats.c b/plugins/solidigm/solidigm-temp-stats.c new file mode 100644 index 0000000..85a3c37 --- /dev/null +++ b/plugins/solidigm/solidigm-temp-stats.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <errno.h> + +#include "common.h" +#include "nvme-print.h" +#include "solidigm-util.h" + +#define SLDGM_TEMP_STATS_LID 0xC5 + +struct temp_stats { + __le64 curr; + __le64 last_overtemp; + __le64 life_overtemp; + __le64 highest_temp; + __le64 lowest_temp; + __u8 rsvd[40]; + __le64 max_operating_temp; + __le64 min_operating_temp; + __le64 est_offset; +}; + +static void show_temp_stats(struct temp_stats *stats) +{ + printf("Current temperature : %"PRIu64"\n", le64_to_cpu(stats->curr)); + printf("Last critical overtemp flag : %"PRIu64"\n", le64_to_cpu(stats->last_overtemp)); + printf("Life critical overtemp flag : %"PRIu64"\n", le64_to_cpu(stats->life_overtemp)); + printf("Highest temperature : %"PRIu64"\n", le64_to_cpu(stats->highest_temp)); + printf("Lowest temperature : %"PRIu64"\n", le64_to_cpu(stats->lowest_temp)); + printf("Max operating temperature : %"PRIu64"\n", le64_to_cpu(stats->max_operating_temp)); + printf("Min operating temperature : %"PRIu64"\n", le64_to_cpu(stats->min_operating_temp)); + printf("Estimated offset : %"PRIu64"\n", le64_to_cpu(stats->est_offset)); +} + +int sldgm_get_temp_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + unsigned char buffer[4096] = {0}; + struct nvme_dev *dev; + __u8 uuid_idx; + int err; + + const char *desc = "Get/show Temperature Statistics log."; + const char *raw = "dump output in binary format"; + struct config { + bool raw_binary; + }; + + struct config cfg = { + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + uuid_idx = solidigm_get_vu_uuid_index(dev); + + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = buffer, + .args_size = sizeof(args), + .fd = dev_fd(dev), + .uuidx = uuid_idx, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = SLDGM_TEMP_STATS_LID, + .len = sizeof(buffer), + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .rae = false, + .ot = false, + }; + + err = nvme_get_log(&args); + if (!err) { + uint64_t *guid = (uint64_t *)&buffer[4080]; + + if (guid[1] == 0xC7BB98B7D0324863 && guid[0] == 0xBB2C23990E9C722F) { + fprintf(stderr, "Error: Log page has 'OCP unsupported Requirements' GUID\n"); + err = -EBADMSG; + goto closefd; + } + if (!cfg.raw_binary) + show_temp_stats((struct temp_stats *) buffer); + else + d_raw(buffer, sizeof(struct temp_stats)); + } else if (err > 0) { + nvme_show_status(err); + } + +closefd: + /* Redundant close() to make static code analysis happy */ + close(dev->direct.fd); + dev_close(dev); + return err; +} diff --git a/plugins/solidigm/solidigm-temp-stats.h b/plugins/solidigm/solidigm-temp-stats.h new file mode 100644 index 0000000..58d5a86 --- /dev/null +++ b/plugins/solidigm/solidigm-temp-stats.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int sldgm_get_temp_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/solidigm/solidigm-util.h b/plugins/solidigm/solidigm-util.h index 3a18501..fa5032f 100644 --- a/plugins/solidigm/solidigm-util.h +++ b/plugins/solidigm/solidigm-util.h @@ -7,4 +7,6 @@ #include "nvme.h" +#define DRIVER_MAX_TX_256K (256 * 1024) + __u8 solidigm_get_vu_uuid_index(struct nvme_dev *dev); diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index ec3f2b0..d663131 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -36,6 +36,7 @@ #include "libnvme.h" #include "plugin.h" #include "linux/types.h" +#include "util/cleanup.h" #include "util/types.h" #include "nvme-print.h" @@ -80,12 +81,14 @@ #define WDC_NVME_SN655_DEV_ID 0x2722 #define WDC_NVME_SN860_DEV_ID 0x2730 #define WDC_NVME_SN660_DEV_ID 0x2704 - -/* This id's are no longer supported, delete ?? */ -#define WDC_NVME_SN550_DEV_ID 0x2708 #define WDC_NVME_SN560_DEV_ID_1 0x2712 #define WDC_NVME_SN560_DEV_ID_2 0x2713 #define WDC_NVME_SN560_DEV_ID_3 0x2714 +#define WDC_NVME_SN861_DEV_ID 0x2750 +#define WDC_NVME_SN861_DEV_ID_1 0x2751 + +/* This id's are no longer supported, delete ?? */ +#define WDC_NVME_SN550_DEV_ID 0x2708 #define WDC_NVME_SXSLCL_DEV_ID 0x2001 #define WDC_NVME_SN520_DEV_ID 0x5003 @@ -147,6 +150,8 @@ #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 | \ @@ -162,6 +167,7 @@ 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 @@ -301,8 +307,8 @@ #define WDC_NVME_GET_STAT_PERF_INTERVAL_LIFETIME 0x0F /* C2 Log Page */ -#define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE 0xC2 -#define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 0xC8 +#define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID 0xC2 +#define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID_C8 0xC8 #define WDC_C2_LOG_BUF_LEN 0x1000 #define WDC_C2_LOG_PAGES_SUPPORTED_ID 0x08 #define WDC_C2_CUSTOMER_ID_ID 0x15 @@ -367,12 +373,12 @@ static __u8 wdc_lat_mon_guid[WDC_C3_GUID_LENGTH] = { #define NVME_LOG_PERSISTENT_EVENT 0x0D #define WDC_LOG_ID_C0 0xC0 #define WDC_LOG_ID_C1 0xC1 -#define WDC_LOG_ID_C2 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE +#define WDC_LOG_ID_C2 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID #define WDC_LOG_ID_C3 0xC3 #define WDC_LOG_ID_C4 0xC4 #define WDC_LOG_ID_C5 0xC5 #define WDC_LOG_ID_C6 0xC6 -#define WDC_LOG_ID_C8 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 +#define WDC_LOG_ID_C8 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID_C8 #define WDC_LOG_ID_CA WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE #define WDC_LOG_ID_CB WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID #define WDC_LOG_ID_D0 WDC_NVME_GET_VU_SMART_LOG_OPCODE @@ -446,6 +452,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, @@ -473,6 +485,27 @@ enum NVME_FEATURE_IDENTIFIERS { FID_RESERVATION_PERSISTENCE = 0x83 }; +/* WDC UUID value */ +const uint8_t WDC_UUID[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0xb9, 0x8c, 0x52, 0x0c, 0x4c, + 0x5a, 0x15, 0xab, 0xe6, 0x33, 0x29, 0x9a, 0x70, 0xdf, 0xd0 +}; + +/* WDC_UUID value for SN640_3 devices */ +const uint8_t WDC_UUID_SN640_3[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 +}; + +/* UUID field with value of 0 indicates end of UUID List*/ +const uint8_t UUID_END[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + enum WDC_DRIVE_ESSENTIAL_TYPE { WDC_DE_TYPE_IDENTIFY = 0x1, WDC_DE_TYPE_SMARTATTRIBUTEDUMP = 0x2, @@ -656,6 +689,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 @@ -793,6 +887,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); @@ -1228,7 +1362,7 @@ struct __packed wdc_fw_act_history_log_format_c2 { struct __packed wdc_ocp_C4_dev_cap_log { __le16 num_pcie_ports; /* 0000 - Number of PCI Express Ports */ __le16 oob_mgmt_support; /* 0002 - OOB Management Interfaces Supported */ - __le16 wrt_zeros_support; /* 0004 - Write Zeros Commmand Support */ + __le16 wrt_zeros_support; /* 0004 - Write Zeros Command Support */ __le16 sanitize_support; /* 0006 - Sanitize Command Support */ __le16 dsm_support; /* 0008 - Dataset Management Command Support */ __le16 wrt_uncor_support; /* 0010 - Write Uncorrectable Command Support */ @@ -1268,6 +1402,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) { @@ -1367,6 +1516,60 @@ static int wdc_get_vendor_id(struct nvme_dev *dev, uint32_t *vendor_id) return ret; } +static bool wdc_is_sn861(__u32 device_id) +{ + if ((device_id == WDC_NVME_SN861_DEV_ID) || + (device_id == WDC_NVME_SN861_DEV_ID_1)) + return true; + else + return false; +} + + +static bool wdc_is_sn640(__u32 device_id) +{ + if ((device_id == WDC_NVME_SN640_DEV_ID) || + (device_id == WDC_NVME_SN640_DEV_ID_1) || + (device_id == WDC_NVME_SN640_DEV_ID_2)) + return true; + else + return false; +} + +static bool wdc_is_sn640_3(__u32 device_id) +{ + if (device_id == WDC_NVME_SN640_DEV_ID_3) + return true; + else + return false; +} + +static bool wdc_is_sn650_u2(__u32 device_id) +{ + if (device_id == WDC_NVME_SN650_DEV_ID_3) + return true; + else + return false; +} + +static bool wdc_is_sn650_e1l(__u32 device_id) +{ + if (device_id == WDC_NVME_SN650_DEV_ID_4) + return true; + else + return false; +} + +static bool needs_c2_log_page_check(__u32 device_id) +{ + if ((wdc_is_sn640(device_id)) || + (wdc_is_sn650_u2(device_id)) || + (wdc_is_sn650_e1l(device_id))) + return true; + else + return false; +} + static bool wdc_check_power_of_2(int num) { return num && (!(num & (num-1))); @@ -1548,7 +1751,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) WDC_DEV_CAP_LOG_ID)) capabilities |= WDC_DRIVE_CAP_OCP_C4_LOG_PAGE; - /* verify the 0xC5 (OCP Unsupported Requirments) log page is supported */ + /* verify the 0xC5 (OCP Unsupported Requirements) log page is supported */ if (wdc_nvme_check_supported_log_page(r, dev, WDC_UNSUPPORTED_REQS_LOG_ID)) capabilities |= WDC_DRIVE_CAP_OCP_C5_LOG_PAGE; @@ -1637,7 +1840,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) if (wdc_nvme_check_supported_log_page(r, dev, WDC_DEV_CAP_LOG_ID)) capabilities |= WDC_DRIVE_CAP_OCP_C4_LOG_PAGE; - /* verify the 0xC5 (OCP Unsupported Requirments) log page is supported */ + /* verify the 0xC5 (OCP Unsupported Requirements) log page is supported */ if (wdc_nvme_check_supported_log_page(r, dev, WDC_UNSUPPORTED_REQS_LOG_ID)) capabilities |= WDC_DRIVE_CAP_OCP_C5_LOG_PAGE; @@ -1666,6 +1869,23 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) WDC_DRIVE_CAP_CLEAR_PCIE); break; + case WDC_NVME_SN861_DEV_ID: + fallthrough; + case WDC_NVME_SN861_DEV_ID_1: + 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; } @@ -1958,7 +2178,7 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id, remaining_len = 0; valid_log = false; - /* The struture is invalid, so any match that was found is invalid. */ + /* The structure is invalid, so any match that was found is invalid. */ *p_p_found_log_entry = NULL; } else { /* Structure must have at least one valid entry to be considered valid */ @@ -1982,33 +2202,25 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id, return valid_log; } -static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev, - __u8 log_id, void **cbs_data) +static bool get_dev_mgmt_log_page_lid_data(struct nvme_dev *dev, + void **cbs_data, + __u8 lid, + __u8 log_id, + __u8 uuid_ix) { - int ret = -1; void *data; struct wdc_c2_log_page_header *hdr_ptr; struct wdc_c2_log_subpage_header *sph; __u32 length = 0; + int ret = 0; bool found = false; - __u8 uuid_ix = 1; - __u8 lid = 0; - *cbs_data = NULL; - __u32 device_id, read_vendor_id; - - ret = wdc_get_pci_ids(r, dev, &device_id, &read_vendor_id); - if (device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) { - lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8; - uuid_ix = 0; - } else { - lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE; - } data = (__u8 *)malloc(sizeof(__u8) * WDC_C2_LOG_BUF_LEN); if (!data) { fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); return false; } + memset(data, 0, sizeof(__u8) * WDC_C2_LOG_BUF_LEN); /* get the log page length */ @@ -2031,7 +2243,9 @@ static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev, }; ret = nvme_get_log(&args_len); if (ret) { - fprintf(stderr, "ERROR: WDC: Unable to get 0x%x Log Page length, ret = 0x%x\n", lid, ret); + fprintf(stderr, + "ERROR: WDC: Unable to get 0x%x Log Page length with uuid %d, ret = 0x%x\n", + lid, uuid_ix, ret); goto end; } @@ -2044,37 +2258,38 @@ static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev, data = calloc(length, sizeof(__u8)); if (!data) { fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); - return false; + goto end; } - } - /* get the log page data */ - struct nvme_get_log_args args_data = { - .args_size = sizeof(args_data), - .fd = dev_fd(dev), - .lid = lid, - .nsid = 0xFFFFFFFF, - .lpo = 0, - .lsp = NVME_LOG_LSP_NONE, - .lsi = 0, - .rae = false, - .uuidx = uuid_ix, - .csi = NVME_CSI_NVM, - .ot = false, - .len = length, - .log = data, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - ret = nvme_get_log(&args_data); + /* get the log page data with the increased length */ + struct nvme_get_log_args args_data = { + .args_size = sizeof(args_data), + .fd = dev_fd(dev), + .lid = lid, + .nsid = 0xFFFFFFFF, + .lpo = 0, + .lsp = NVME_LOG_LSP_NONE, + .lsi = 0, + .rae = false, + .uuidx = uuid_ix, + .csi = NVME_CSI_NVM, + .ot = false, + .len = length, + .log = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + ret = nvme_get_log(&args_data); - if (ret) { - fprintf(stderr, "ERROR: WDC: Unable to read 0x%x Log Page data, ret = 0x%x\n", lid, ret); - goto end; + if (ret) { + fprintf(stderr, + "ERROR: WDC: Unable to read 0x%x Log Page data with uuid %d, ret = 0x%x\n", + lid, uuid_ix, ret); + goto end; + } } /* Check the log data to see if the WD version of log page ID's is found */ - length = sizeof(struct wdc_c2_log_page_header); hdr_ptr = (struct wdc_c2_log_page_header *)data; sph = (struct wdc_c2_log_subpage_header *)(data + length); @@ -2083,50 +2298,91 @@ static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev, *cbs_data = calloc(le32_to_cpu(sph->length), sizeof(__u8)); if (!*cbs_data) { fprintf(stderr, "ERROR: WDC: calloc: %s\n", strerror(errno)); + found = false; goto end; } memcpy((void *)*cbs_data, (void *)&sph->data, le32_to_cpu(sph->length)); } else { - /* not found with uuid = 1 try with uuid = 0 */ - uuid_ix = 0; - /* get the log page data */ - struct nvme_get_log_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .lid = lid, - .nsid = 0xFFFFFFFF, - .lpo = 0, - .lsp = NVME_LOG_LSP_NONE, - .lsi = 0, - .rae = false, - .uuidx = uuid_ix, - .csi = NVME_CSI_NVM, - .ot = false, - .len = le32_to_cpu(hdr_ptr->length), - .log = data, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - ret = nvme_get_log(&args); + fprintf(stderr, "ERROR: WDC: C2 log id 0x%x not found with uuid index %d\n", + log_id, uuid_ix); + } - hdr_ptr = (struct wdc_c2_log_page_header *)data; - sph = (struct wdc_c2_log_subpage_header *)(data + length); - found = wdc_get_dev_mng_log_entry(le32_to_cpu(hdr_ptr->length), log_id, hdr_ptr, &sph); - if (found) { - *cbs_data = calloc(le32_to_cpu(sph->length), sizeof(__u8)); - if (!*cbs_data) { - fprintf(stderr, "ERROR: WDC: calloc: %s\n", strerror(errno)); - goto end; - } - memcpy((void *)*cbs_data, (void *)&sph->data, le32_to_cpu(sph->length)); +end: + free(data); + return found; +} + +static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev, + __u8 log_id, void **cbs_data) +{ + int ret = -1; + bool found = false; + __u8 uuid_ix = 0; + __u8 lid = 0; + *cbs_data = NULL; + __u32 device_id, read_vendor_id; + bool uuid_present = false; + int index = 0, uuid_index = 0; + struct nvme_id_uuid_list uuid_list; + + ret = wdc_get_pci_ids(r, dev, &device_id, &read_vendor_id); + if (ret == 0) { + if (device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) { + lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID_C8; + uuid_ix = 0; } else { - /* WD version not found */ - fprintf(stderr, "ERROR: WDC: Unable to find correct version of page 0x%x, entry id = %d\n", lid, log_id); + lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID; } + } else { + fprintf(stderr, "ERROR: WDC: get pci ids: %d\n", ret); + return false; + } + + typedef struct nvme_id_uuid_list_entry *uuid_list_entry; + + memset(&uuid_list, 0, sizeof(struct nvme_id_uuid_list)); + if (wdc_CheckUuidListSupport(dev, &uuid_list)) { + uuid_list_entry uuid_list_entry_ptr = (uuid_list_entry)&uuid_list.entry[0]; + + while (index <= NVME_ID_UUID_LIST_MAX && + !wdc_UuidEqual(uuid_list_entry_ptr, (uuid_list_entry)UUID_END)) { + + if (wdc_UuidEqual(uuid_list_entry_ptr, + (uuid_list_entry)WDC_UUID)) { + uuid_present = true; + break; + } else if (wdc_UuidEqual(uuid_list_entry_ptr, + (uuid_list_entry)WDC_UUID_SN640_3) && + wdc_is_sn640_3(device_id)) { + uuid_present = true; + break; + } + index++; + uuid_list_entry_ptr = (uuid_list_entry)&uuid_list.entry[index]; + } + if (uuid_present) + uuid_index = index + 1; + } + + if (!uuid_index && needs_c2_log_page_check(device_id)) { + /* In certain devices that don't support UUID lists, there are multiple + * definitions of the C2 logpage. In those cases, the code + * needs to try two UUID indexes and use an identification algorithm + * to determine which is returning the correct log page data. + */ + uuid_ix = 1; + } + + found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_ix); + + if (!found) { + /* not found with uuid = 1 try with uuid = 0 */ + uuid_ix = 0; + fprintf(stderr, "Not found, requesting log page with uuid_index %d\n", uuid_index); + + found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_ix); } -end: - free(data); return found; } @@ -3373,6 +3629,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) { @@ -3385,6 +3737,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; @@ -3393,6 +3746,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 { @@ -3441,72 +3797,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 (!wdc_is_sn861(device_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 (cfg.data_area) { - if (cfg.data_area > 5 || cfg.data_area < 1) { - fprintf(stderr, "ERROR: WDC: Data area must be 1-5\n"); + 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); + + + 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 (!wdc_is_sn861(device_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 = wdc_do_cap_diag(r, dev, f, xfer_size, - telemetry_type, telemetry_data_area); + 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 = 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) { @@ -4362,7 +4795,7 @@ static void wdc_print_dev_cap_log_normal(struct wdc_ocp_C4_dev_cap_log *log_data printf(" Minimum DSSD Power State : 0x%x\n", le16_to_cpu(log_data->min_dssd_ps)); for (j = 0; j < WDC_OCP_C4_NUM_PS_DESCR; j++) - printf(" DSSD Power State %d Desriptor : 0x%x\n", j, log_data->dssd_ps_descr[j]); + printf(" DSSD Power State %d Descriptor : 0x%x\n", j, log_data->dssd_ps_descr[j]); printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version)); printf(" Log page GUID : 0x"); @@ -5006,7 +5439,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]; @@ -5020,7 +5455,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 || + wdc_is_sn861(device_id)) { printf(" Power on Hour Power Cycle Previous New\n"); printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result\n"); printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); @@ -5079,6 +5516,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 (wdc_is_sn861(device_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)); @@ -5177,7 +5634,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; @@ -5186,11 +5645,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; @@ -5242,6 +5703,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 (wdc_is_sn861(device_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)); @@ -6071,7 +6543,7 @@ static void wdc_print_smart_cloud_attr_C0_normal(void *data) (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); printf(" Unaligned I/O : %"PRIu64"\n", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); - printf(" Securqity Version Number : %"PRIu64"\n", + printf(" Security Version Number : %"PRIu64"\n", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); printf(" NUSE Namespace utilization : %"PRIu64"\n", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); @@ -6465,17 +6937,19 @@ static int wdc_get_c0_log_page_sn(nvme_root_t r, struct nvme_dev *dev, int uuid_ static int wdc_get_c0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format, int uuid_index, __u32 namespace_id) { - int ret = 0; - int fmt = -1; - __u8 *data; uint32_t device_id, read_vendor_id; + enum nvme_print_flags fmt; + int ret; + __u8 *data; + __u8 log_id; + __u32 length; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } ret = wdc_get_pci_ids(r, dev, &device_id, &read_vendor_id); @@ -6495,6 +6969,16 @@ static int wdc_get_c0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format fallthrough; case WDC_NVME_SN860_DEV_ID: fallthrough; + case WDC_NVME_SN560_DEV_ID_1: + fallthrough; + case WDC_NVME_SN560_DEV_ID_2: + fallthrough; + case WDC_NVME_SN560_DEV_ID_3: + fallthrough; + case WDC_NVME_SN550_DEV_ID: + ret = wdc_get_c0_log_page_sn(r, dev, uuid_index, format, namespace_id, fmt); + break; + case WDC_NVME_SN650_DEV_ID: fallthrough; case WDC_NVME_SN650_DEV_ID_1: @@ -6506,16 +6990,63 @@ static int wdc_get_c0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format case WDC_NVME_SN650_DEV_ID_4: fallthrough; case WDC_NVME_SN655_DEV_ID: - fallthrough; - case WDC_NVME_SN560_DEV_ID_1: - fallthrough; - case WDC_NVME_SN560_DEV_ID_2: - fallthrough; - case WDC_NVME_SN560_DEV_ID_3: - fallthrough; - case WDC_NVME_SN550_DEV_ID: - ret = wdc_get_c0_log_page_sn(r, dev, uuid_index, format, namespace_id, fmt); + if (uuid_index == 0) { + log_id = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID; + length = WDC_NVME_SMART_CLOUD_ATTR_LEN; + } else { + log_id = WDC_NVME_GET_EOL_STATUS_LOG_OPCODE; + length = WDC_NVME_EOL_STATUS_LOG_LEN; + } + + data = (__u8 *)malloc(sizeof(__u8) * length); + if (!data) { + fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); + return -1; + } + + if (namespace_id == NVME_NSID_ALL) { + ret = nvme_get_nsid(dev_fd(dev), &namespace_id); + if (ret < 0) + namespace_id = NVME_NSID_ALL; + } + + /* Get the 0xC0 log data */ + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .lid = log_id, + .nsid = namespace_id, + .lpo = 0, + .lsp = NVME_LOG_LSP_NONE, + .lsi = 0, + .rae = false, + .uuidx = uuid_index, + .csi = NVME_CSI_NVM, + .ot = false, + .len = length, + .log = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + ret = nvme_get_log(&args); + + if (strcmp(format, "json")) + nvme_show_status(ret); + + if (!ret) { + /* parse the data */ + if (uuid_index == 0) + wdc_print_c0_cloud_attr_log(data, fmt); + else + wdc_print_c0_eol_log(data, fmt); + } else { + fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data "); + fprintf(stderr, "with uuid index %d\n", uuid_index); + ret = -1; + } + free(data); break; + case WDC_NVME_ZN350_DEV_ID: fallthrough; case WDC_NVME_ZN350_DEV_ID_1: @@ -6697,7 +7228,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"); @@ -6706,10 +7239,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; @@ -6717,19 +7252,19 @@ static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt, __ static int wdc_get_ca_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; - __u8 *data; - struct wdc_ssd_ca_perf_stats *perf; uint32_t read_device_id, read_vendor_id; + struct wdc_ssd_ca_perf_stats *perf; + enum nvme_print_flags fmt; __u32 cust_id; + __u8 *data; + int ret; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } /* verify the 0xCA log page is supported */ @@ -6853,23 +7388,24 @@ static int wdc_get_ca_log_page(nvme_root_t r, struct nvme_dev *dev, char *format static int wdc_get_c1_log_page(nvme_root_t r, struct nvme_dev *dev, char *format, uint8_t interval) { - int ret = 0; - int fmt = -1; + struct wdc_log_page_subpage_header *sph; + struct wdc_ssd_perf_stats *perf; + struct wdc_log_page_header *l; + enum nvme_print_flags fmt; + int total_subpages; + int skip_cnt = 4; __u8 *data; __u8 *p; int i; - int skip_cnt = 4; - int total_subpages; - struct wdc_log_page_header *l; - struct wdc_log_page_subpage_header *sph; - struct wdc_ssd_perf_stats *perf; + int ret; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } if (interval < 1 || interval > 15) { @@ -6911,18 +7447,19 @@ static int wdc_get_c1_log_page(nvme_root_t r, struct nvme_dev *dev, static int wdc_get_c3_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; + struct wdc_ssd_latency_monitor_log *log_data; + enum nvme_print_flags fmt; __u8 *data; + int ret; int i; - struct wdc_ssd_latency_monitor_log *log_data; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } data = (__u8 *)malloc(sizeof(__u8) * WDC_LATENCY_MON_LOG_BUF_LEN); @@ -6982,18 +7519,19 @@ out: static int wdc_get_ocp_c1_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; + struct wdc_ocp_c1_error_recovery_log *log_data; + enum nvme_print_flags fmt; __u8 *data; + int ret; int i; - struct wdc_ocp_c1_error_recovery_log *log_data; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } data = (__u8 *)malloc(sizeof(__u8) * WDC_ERROR_REC_LOG_BUF_LEN); @@ -7052,18 +7590,19 @@ out: static int wdc_get_ocp_c4_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; + struct wdc_ocp_C4_dev_cap_log *log_data; + enum nvme_print_flags fmt; __u8 *data; + int ret; int i; - struct wdc_ocp_C4_dev_cap_log *log_data; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } data = (__u8 *)malloc(sizeof(__u8) * WDC_DEV_CAP_LOG_BUF_LEN); @@ -7121,18 +7660,19 @@ out: static int wdc_get_ocp_c5_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; + struct wdc_ocp_C5_unsupported_reqs *log_data; + enum nvme_print_flags fmt; + int ret; __u8 *data; int i; - struct wdc_ocp_C5_unsupported_reqs *log_data; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } data = (__u8 *)malloc(sizeof(__u8) * WDC_UNSUPPORTED_REQS_LOG_BUF_LEN); @@ -7190,17 +7730,18 @@ out: static int wdc_get_d0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { + struct wdc_ssd_d0_smart_log *perf; + enum nvme_print_flags fmt; int ret = 0; - int fmt = -1; __u8 *data; - struct wdc_ssd_d0_smart_log *perf; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } /* verify the 0xD0 log page is supported */ @@ -7235,6 +7776,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) { @@ -7243,6 +8013,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, const char *log_page_version = "Log Page Version: 0 = vendor, 1 = WDC"; const char *log_page_mask = "Log Page Mask, comma separated list: 0xC0, 0xC1, 0xCA, 0xD0"; const char *namespace_id = "desired namespace id"; + enum nvme_print_flags fmt; struct nvme_dev *dev; nvme_root_t r; int ret = 0; @@ -7250,6 +8021,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, int page_mask = 0, num, i; int log_page_list[16]; __u64 capabilities = 0; + __u32 device_id, read_vendor_id; struct config { uint8_t interval; @@ -7269,7 +8041,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), @@ -7317,8 +8089,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; @@ -7328,13 +8101,56 @@ 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 (!wdc_is_sn861(device_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]; + + ret = validate_output_format(output_format, &fmt); + if (ret < 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) && + (!wdc_is_sn861(device_id))) { /* Get the CA Log Page */ ret = wdc_get_ca_log_page(r, dev, cfg.output_format); if (ret) @@ -7367,10 +8183,11 @@ static int wdc_vs_cloud_log(int argc, char **argv, struct command *command, { const char *desc = "Retrieve Cloud Log Smart/Health Information"; const char *namespace_id = "desired namespace id"; + enum nvme_print_flags fmt; __u64 capabilities = 0; struct nvme_dev *dev; - int ret, fmt = -1; nvme_root_t r; + int ret; __u8 *data; struct config { @@ -7411,14 +8228,13 @@ static int wdc_vs_cloud_log(int argc, char **argv, struct command *command, nvme_show_status(ret); if (!ret) { - fmt = validate_output_format(cfg.output_format); - if (fmt < 0) { + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC %s: invalid output format\n", __func__); - ret = fmt; + } else { + /* parse the data */ + wdc_print_ext_smart_cloud_log(data, fmt); } - - /* parse the data */ - wdc_print_ext_smart_cloud_log(data, fmt); } else { fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page V1 data\n"); ret = -1; @@ -7438,9 +8254,10 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *command, { const char *desc = "Retrieve Hardware Revision Log Information"; const char *namespace_id = "desired namespace id"; + enum nvme_print_flags fmt; __u64 capabilities = 0; struct nvme_dev *dev; - int ret, fmt = -1; + int ret; __u8 *data = NULL; nvme_root_t r; @@ -7480,10 +8297,9 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *command, nvme_show_status(ret); if (!ret) { - fmt = validate_output_format(cfg.output_format); - if (fmt < 0) { + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC %s: invalid output format\n", __func__); - ret = fmt; goto free_buf; } @@ -7499,6 +8315,8 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *command, case JSON: wdc_print_hw_rev_log_json(data); break; + default: + break; } } else { fprintf(stderr, "ERROR: WDC: Unable to read Hardware Revision Log Page data\n"); @@ -7521,11 +8339,11 @@ static int wdc_vs_device_waf(int argc, char **argv, struct command *command, const char *desc = "Retrieve Device Write Amplication Factor"; const char *namespace_id = "desired namespace id"; struct nvme_smart_log smart_log; + enum nvme_print_flags fmt; struct nvme_dev *dev; __u8 *data; nvme_root_t r; int ret = 0; - int fmt = -1; __u64 capabilities = 0; struct __packed wdc_nvme_ext_smart_log * ext_smart_log_ptr; long double data_units_written = 0, @@ -7601,10 +8419,9 @@ static int wdc_vs_device_waf(int argc, char **argv, struct command *command, if (strcmp(cfg.output_format, "json")) nvme_show_status(ret); - fmt = validate_output_format(cfg.output_format); - if (fmt < 0) { + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC %s: invalid output format\n", __func__); - ret = fmt; goto out; } @@ -7936,7 +8753,7 @@ static int wdc_drive_status(int argc, char **argv, struct command *command, /* verify the 0xC2 Device Manageability log page is supported */ if (wdc_nvme_check_supported_log_page(r, dev, - WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE) == false) { + WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID) == false) { fprintf(stderr, "ERROR: WDC: 0xC2 Log Page not supported\n"); ret = -1; goto out; @@ -8078,18 +8895,18 @@ out: static int wdc_get_fw_act_history(nvme_root_t r, struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; - __u8 *data; struct wdc_fw_act_history_log_hdr *fw_act_history_hdr; + enum nvme_print_flags fmt; + int ret; + __u8 *data; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } /* verify the FW Activate History log page is supported */ @@ -8121,7 +8938,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; @@ -8159,21 +8976,21 @@ static __u32 wdc_get_fw_cust_id(nvme_root_t r, struct nvme_dev *dev) static int wdc_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev, char *format) { - int ret = 0; - int fmt = -1; - __u8 *data; - __u32 cust_id; 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; + __u32 cust_id = 0; + enum nvme_print_flags fmt; + __u8 *data; + int ret; if (!wdc_check_device(r, dev)) return -1; - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - return fmt; + return ret; } ret = wdc_get_pci_ids(r, dev, &device_id, &vendor_id); @@ -8200,15 +9017,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 (!wdc_is_sn861(device_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; @@ -9437,7 +10259,7 @@ static int wdc_reason_identifier(int argc, char **argv, cfg.log_id != NVME_LOG_LID_TELEMETRY_CTRL) { fprintf(stderr, "ERROR: WDC: Invalid Log ID. It must be 7 (Host) or 8 (Controller)\n"); ret = -1; - goto close_fd; + goto close_dev; } if (cfg.file) { @@ -9448,7 +10270,7 @@ static int wdc_reason_identifier(int argc, char **argv, if (verify_file < 0) { fprintf(stderr, "ERROR: WDC: open: %s\n", strerror(errno)); ret = -1; - goto close_fd; + goto close_dev; } close(verify_file); strncpy(f, cfg.file, PATH_MAX - 1); @@ -9466,12 +10288,12 @@ static int wdc_reason_identifier(int argc, char **argv, if (wdc_get_serial_name(dev, f, PATH_MAX, fileSuffix) == -1) { fprintf(stderr, "ERROR: WDC: failed to generate file name\n"); ret = -1; - goto close_fd; + goto close_dev; } if (strlen(f) > PATH_MAX - 5) { fprintf(stderr, "ERROR: WDC: file name overflow\n"); ret = -1; - goto close_fd; + goto close_dev; } strcat(f, ".bin"); } @@ -9488,7 +10310,7 @@ static int wdc_reason_identifier(int argc, char **argv, nvme_show_status(ret); -close_fd: +close_dev: dev_close(dev); nvme_free_tree(r); return ret; @@ -9570,10 +10392,104 @@ 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) { const char *desc = "Retrieve Log Page Directory."; + enum nvme_print_flags fmt; struct nvme_dev *dev; int ret = 0; nvme_root_t r; @@ -9582,6 +10498,8 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command int i; __u8 log_id = 0; __u32 device_id, read_vendor_id; + bool uuid_supported = false; + struct nvme_id_uuid_list uuid_list; struct config { char *output_format; @@ -9600,13 +10518,12 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command if (ret) return ret; - ret = validate_output_format(cfg.output_format); + ret = validate_output_format(cfg.output_format, &fmt); if (ret < 0) { fprintf(stderr, "%s: ERROR: WDC: invalid output format\n", __func__); dev_close(dev); return ret; } - ret = 0; r = nvme_scan(NULL); capabilities = wdc_get_drive_capabilities(r, dev); @@ -9615,54 +10532,110 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command fprintf(stderr, "ERROR: WDC: unsupported device for this command\n"); 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]); + memset(&uuid_list, 0, sizeof(struct nvme_id_uuid_list)); + if (wdc_CheckUuidListSupport(dev, &uuid_list)) + uuid_supported = true; - 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 (uuid_supported) + fprintf(stderr, "WDC: UUID lists supported\n"); + else + fprintf(stderr, "WDC: UUID lists NOT supported\n"); + + + 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_ID_C8 : + WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID; + + if (!wdc_is_sn861(device_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; + } + + 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) { + switch (fmt) { + case BINARY: + d_raw((unsigned char *)data, 32); + break; + case JSON: + __json_log_page_directory(dir); + break; + default: + __show_log_page_directory(dir); + } + } else { + fprintf(stderr, "NVMe Status:%s(%x)\n", + nvme_status_to_string(ret, false), ret); + } + } } out: @@ -10176,22 +11149,21 @@ static void wdc_print_pcie_stats_json(struct wdc_vs_pcie_stats *pcie_stats) static int wdc_do_vs_nand_stats_sn810_2(struct nvme_dev *dev, char *format) { - int ret; - int fmt = -1; + enum nvme_print_flags fmt; uint8_t *data = NULL; + int ret; data = NULL; ret = nvme_get_ext_smart_cloud_log(dev_fd(dev), &data, 0, NVME_NSID_ALL); if (ret) { - fprintf(stderr, "ERROR: WDC: %s : Failed to retreive NAND stats\n", __func__); + fprintf(stderr, "ERROR: WDC: %s : Failed to retrieve NAND stats\n", __func__); goto out; } else { - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: %s : invalid output format\n", __func__); - ret = fmt; goto out; } @@ -10203,6 +11175,8 @@ static int wdc_do_vs_nand_stats_sn810_2(struct nvme_dev *dev, char *format) case JSON: wdc_print_ext_smart_cloud_log_json(data, WDC_SCA_V1_NAND_STATS); break; + default: + break; } } @@ -10214,10 +11188,10 @@ out: static int wdc_do_vs_nand_stats(struct nvme_dev *dev, char *format) { - int ret; - int fmt = -1; + enum nvme_print_flags fmt; uint8_t *output = NULL; __u16 version = 0; + int ret; output = (uint8_t *)calloc(WDC_NVME_NAND_STATS_SIZE, sizeof(uint8_t)); if (!output) { @@ -10229,13 +11203,12 @@ static int wdc_do_vs_nand_stats(struct nvme_dev *dev, char *format) ret = nvme_get_log_simple(dev_fd(dev), WDC_NVME_NAND_STATS_LOG_ID, WDC_NVME_NAND_STATS_SIZE, (void *)output); if (ret) { - fprintf(stderr, "ERROR: WDC: %s : Failed to retreive NAND stats\n", __func__); + fprintf(stderr, "ERROR: WDC: %s : Failed to retrieve NAND stats\n", __func__); goto out; } else { - fmt = validate_output_format(format); - if (fmt < 0) { + ret = validate_output_format(format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - ret = fmt; goto out; } @@ -10249,6 +11222,8 @@ static int wdc_do_vs_nand_stats(struct nvme_dev *dev, char *format) case JSON: wdc_print_nand_stats_json(version, output); break; + default: + break; } } @@ -10337,14 +11312,14 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Retrieve PCIE statistics."; + enum nvme_print_flags fmt; struct nvme_dev *dev; - int ret = 0; nvme_root_t r; + int ret; __u64 capabilities = 0; - int fmt = -1; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; struct wdc_vs_pcie_stats *pcieStatsPtr = NULL; int pcie_stats_size = sizeof(struct wdc_vs_pcie_stats); - bool huge; struct config { char *output_format; @@ -10363,16 +11338,14 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, if (ret) return ret; - r = nvme_scan(NULL); - fmt = validate_output_format(cfg.output_format); - if (fmt < 0) { + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - ret = fmt; goto out; } - pcieStatsPtr = nvme_alloc(pcie_stats_size, &huge); + pcieStatsPtr = nvme_alloc_huge(pcie_stats_size, &mh); if (!pcieStatsPtr) { fprintf(stderr, "ERROR: WDC: PCIE Stats alloc: %s\n", strerror(errno)); ret = -1; @@ -10399,12 +11372,11 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, case JSON: wdc_print_pcie_stats_json(pcieStatsPtr); break; + default: + break; } } } - - nvme_free(pcieStatsPtr, huge); - out: nvme_free_tree(r); dev_close(dev); @@ -10415,6 +11387,7 @@ static int wdc_vs_drive_info(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Send a vs-drive-info command."; + enum nvme_print_flags fmt; nvme_root_t r; uint64_t capabilities = 0; struct nvme_dev *dev; @@ -10428,12 +11401,14 @@ static int wdc_vs_drive_info(int argc, char **argv, __u8 *data = NULL; __u32 ftl_unit_size = 0, tcg_dev_ownership = 0; __u16 boot_spec_major = 0, boot_spec_minor = 0; - int fmt = -1; struct json_object *root = NULL; char formatter[41] = { 0 }; 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; @@ -10452,11 +11427,11 @@ static int wdc_vs_drive_info(int argc, char **argv, if (ret) return ret; - fmt = validate_output_format(cfg.output_format); - if (fmt < 0) { + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC %s invalid output format\n", __func__); dev_close(dev); - return fmt; + return ret; } /* get the id ctrl data used to fill in drive info below */ @@ -10502,13 +11477,13 @@ static int wdc_vs_drive_info(int argc, char **argv, rev = (double)(cpu_to_le32(result) & 0x0000ffff); if (fmt == NORMAL) { - printf("Drive HW Revison: %4.1f\n", (.1 * rev)); + printf("Drive HW Revision: %4.1f\n", (.1 * rev)); printf("FTL Unit Size: 0x%x KB\n", size); printf("Customer SN: %-.*s\n", (int)sizeof(ctrl.sn), &ctrl.sn[0]); } else if (fmt == JSON) { root = json_create_object(); sprintf(rev_str, "%4.1f", (.1 * rev)); - json_object_add_value_string(root, "Drive HW Revison", rev_str); + json_object_add_value_string(root, "Drive HW Revision", rev_str); json_object_add_value_int(root, "FTL Unit Size", le16_to_cpu(size)); wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], sizeof(ctrl.sn)); @@ -10611,6 +11586,52 @@ static int wdc_vs_drive_info(int argc, char **argv, } break; + case WDC_NVME_SN861_DEV_ID: + fallthrough; + case WDC_NVME_SN861_DEV_ID_1: + 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"); ret = -1; @@ -10634,12 +11655,13 @@ static int wdc_vs_temperature_stats(int argc, char **argv, const char *desc = "Send a vs-temperature-stats command."; struct nvme_smart_log smart_log; struct nvme_id_ctrl id_ctrl; + enum nvme_print_flags fmt; struct nvme_dev *dev; nvme_root_t r; uint64_t capabilities = 0; __u32 hctm_tmt; int temperature, temp_tmt1, temp_tmt2; - int ret, fmt = -1; + int ret; struct config { char *output_format; @@ -10659,10 +11681,9 @@ static int wdc_vs_temperature_stats(int argc, char **argv, return ret; r = nvme_scan(NULL); - fmt = validate_output_format(cfg.output_format); - if (fmt < 0) { + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0) { fprintf(stderr, "ERROR: WDC: invalid output format\n"); - ret = fmt; goto out; } @@ -10844,6 +11865,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); @@ -11214,3 +12237,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; +} diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h index cdd9615..f3f28a7 100644 --- a/plugins/wdc/wdc-nvme.h +++ b/plugins/wdc/wdc-nvme.h @@ -5,7 +5,7 @@ #if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ) #define WDC_NVME -#define WDC_PLUGIN_VERSION "2.3.1" +#define WDC_PLUGIN_VERSION "2.3.5" #include "cmd.h" PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERSION), @@ -17,34 +17,72 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERS ENTRY("id-ctrl", "WDC identify controller", wdc_id_ctrl) ENTRY("purge", "WDC Purge", wdc_purge) ENTRY("purge-monitor", "WDC Purge Monitor", wdc_purge_monitor) - ENTRY("vs-internal-log", "WDC Internal Firmware Log", wdc_vs_internal_fw_log) + ENTRY("vs-internal-log", "WDC Internal Firmware Log", + wdc_vs_internal_fw_log) ENTRY("vs-nand-stats", "WDC NAND Statistics", wdc_vs_nand_stats) - ENTRY("vs-smart-add-log", "WDC Additional Smart Log", wdc_vs_smart_add_log) - ENTRY("clear-pcie-correctable-errors", "WDC Clear PCIe Correctable Error Count", wdc_clear_pcie_correctable_errors) - ENTRY("drive-essentials", "WDC Drive Essentials", wdc_drive_essentials) - ENTRY("get-drive-status", "WDC Get Drive Status", wdc_drive_status) - ENTRY("clear-assert-dump", "WDC Clear Assert Dump", wdc_clear_assert_dump) + ENTRY("vs-smart-add-log", "WDC Additional Smart Log", + wdc_vs_smart_add_log) + ENTRY("clear-pcie-correctable-errors", + "WDC Clear PCIe Correctable Error Count", + wdc_clear_pcie_correctable_errors) + ENTRY("drive-essentials", "WDC Drive Essentials", + wdc_drive_essentials) + ENTRY("get-drive-status", "WDC Get Drive Status", + wdc_drive_status) + ENTRY("clear-assert-dump", "WDC Clear Assert Dump", + wdc_clear_assert_dump) ENTRY("drive-resize", "WDC Drive Resize", wdc_drive_resize) - ENTRY("vs-fw-activate-history", "WDC Get FW Activate History", wdc_vs_fw_activate_history) - ENTRY("clear-fw-activate-history", "WDC Clear FW Activate History", wdc_clear_fw_activate_history) + ENTRY("vs-fw-activate-history", "WDC Get FW Activate History", + wdc_vs_fw_activate_history) + ENTRY("clear-fw-activate-history", + "WDC Clear FW Activate History", + wdc_clear_fw_activate_history) ENTRY("enc-get-log", "WDC Get Enclosure Log", wdc_enc_get_log) - ENTRY("vs-telemetry-controller-option", "WDC Enable/Disable Controller Initiated Telemetry Log", wdc_vs_telemetry_controller_option) - ENTRY("vs-error-reason-identifier", "WDC Telemetry Reason Identifier", wdc_reason_identifier) - ENTRY("log-page-directory", "WDC Get Log Page Directory", wdc_log_page_directory) - ENTRY("namespace-resize", "WDC NamespaceDrive Resize", wdc_namespace_resize) + ENTRY("vs-telemetry-controller-option", + "WDC Enable/Disable Controller Initiated Telemetry Log", + wdc_vs_telemetry_controller_option) + ENTRY("vs-error-reason-identifier", + "WDC Telemetry Reason Identifier", + wdc_reason_identifier) + ENTRY("log-page-directory", "WDC Get Log Page Directory", + wdc_log_page_directory) + ENTRY("namespace-resize", "WDC NamespaceDrive Resize", + wdc_namespace_resize) ENTRY("vs-drive-info", "WDC Get Drive Info", wdc_vs_drive_info) - ENTRY("vs-temperature-stats", "WDC Get Temperature Stats", wdc_vs_temperature_stats) - ENTRY("capabilities", "WDC Device Capabilities", wdc_capabilities) - ENTRY("cloud-SSD-plugin-version", "WDC Cloud SSD Plugin Version", wdc_cloud_ssd_plugin_version) - ENTRY("vs-pcie-stats", "WDC VS PCIE Statistics", wdc_vs_pcie_stats) - ENTRY("get-latency-monitor-log", "WDC Get Latency Monitor Log Page", wdc_get_latency_monitor_log) - ENTRY("get-error-recovery-log", "WDC Get Error Recovery Log Page", wdc_get_error_recovery_log) - ENTRY("get-dev-capabilities-log", "WDC Get Device Capabilities Log Page", wdc_get_dev_capabilities_log) - ENTRY("get-unsupported-reqs-log", "WDC Get Unsupported Requirements Log Page", wdc_get_unsupported_reqs_log) - ENTRY("cloud-boot-SSD-version", "WDC Get the Cloud Boot SSD Version", wdc_cloud_boot_SSD_version) - ENTRY("vs-cloud-log", "WDC Get the Cloud Log Page", wdc_vs_cloud_log) - ENTRY("vs-hw-rev-log", "WDC Get the Hardware Revision Log Page", wdc_vs_hw_rev_log) - ENTRY("vs-device-waf", "WDC Calculate Device Write Amplication Factor", wdc_vs_device_waf) + ENTRY("vs-temperature-stats", "WDC Get Temperature Stats", + wdc_vs_temperature_stats) + ENTRY("capabilities", "WDC Device Capabilities", + wdc_capabilities) + ENTRY("cloud-SSD-plugin-version", + "WDC Cloud SSD Plugin Version", + wdc_cloud_ssd_plugin_version) + ENTRY("vs-pcie-stats", "WDC VS PCIE Statistics", + wdc_vs_pcie_stats) + ENTRY("get-latency-monitor-log", + "WDC Get Latency Monitor Log Page", + wdc_get_latency_monitor_log) + ENTRY("get-error-recovery-log", + "WDC Get Error Recovery Log Page", + wdc_get_error_recovery_log) + ENTRY("get-dev-capabilities-log", + "WDC Get Device Capabilities Log Page", + wdc_get_dev_capabilities_log) + ENTRY("get-unsupported-reqs-log", + "WDC Get Unsupported Requirements Log Page", + wdc_get_unsupported_reqs_log) + ENTRY("cloud-boot-SSD-version", + "WDC Get the Cloud Boot SSD Version", + wdc_cloud_boot_SSD_version) + ENTRY("vs-cloud-log", "WDC Get the Cloud Log Page", + wdc_vs_cloud_log) + ENTRY("vs-hw-rev-log", "WDC Get the Hardware Revision Log Page", + wdc_vs_hw_rev_log) + 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) ) ); diff --git a/plugins/wdc/wdc-utils.c b/plugins/wdc/wdc-utils.c index 3b60772..be2ef06 100644 --- a/plugins/wdc/wdc-utils.c +++ b/plugins/wdc/wdc-utils.c @@ -24,6 +24,9 @@ #include <string.h> #include <unistd.h> #include <time.h> +#include "nvme.h" +#include "libnvme.h" +#include "nvme-print.h" #include "wdc-utils.h" int wdc_UtilsSnprintf(char *buffer, unsigned int sizeOfBuffer, const char *format, ...) @@ -133,8 +136,8 @@ end: /** * Compares the strings ignoring their cases. * - * @param pcSrc Points to a null terminated string for comapring. - * @param pcDst Points to a null terminated string for comapring. + * @param pcSrc Points to a null terminated string for comparing. + * @param pcDst Points to a null terminated string for comparing. * * @returns zero if the string matches or * 1 if the pcSrc string is lexically higher than pcDst or @@ -162,3 +165,42 @@ void wdc_StrFormat(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz) } } +bool wdc_CheckUuidListSupport(struct nvme_dev *dev, struct nvme_id_uuid_list *uuid_list) +{ + int err; + struct nvme_id_ctrl ctrl; + + memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err) { + fprintf(stderr, "ERROR: WDC: nvme_identify_ctrl() failed 0x%x\n", err); + return false; + } + + if ((ctrl.ctratt & NVME_CTRL_CTRATT_UUID_LIST) == NVME_CTRL_CTRATT_UUID_LIST) { + err = nvme_identify_uuid(dev_fd(dev), uuid_list); + if (!err) + return true; + else if (err > 0) + nvme_show_status(err); + else + nvme_show_error("identify UUID list: %s", nvme_strerror(errno)); + } + + return false; +} + +bool wdc_UuidEqual(struct nvme_id_uuid_list_entry *entry1, struct nvme_id_uuid_list_entry *entry2) +{ + int i; + + for (i = 0; i < 16; i++) { + if (entry1->uuid[i] != entry2->uuid[i]) + break; + } + + if (i == 16) + return true; + else + return false; +} diff --git a/plugins/wdc/wdc-utils.h b/plugins/wdc/wdc-utils.h index 83b208e..3fc47ca 100644 --- a/plugins/wdc/wdc-utils.h +++ b/plugins/wdc/wdc-utils.h @@ -31,6 +31,7 @@ #include <sys/time.h> #include <sys/stat.h> +#include <stdbool.h> #include <string.h> #include <unistd.h> @@ -76,4 +77,5 @@ int wdc_UtilsStrCompare(char *pcSrc, char *pcDst); int wdc_UtilsCreateDir(char *path); int wdc_WriteToFile(char *fileName, char *buffer, unsigned int bufferLen); void wdc_StrFormat(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz); - +bool wdc_CheckUuidListSupport(struct nvme_dev *dev, struct nvme_id_uuid_list *uuid_list); +bool wdc_UuidEqual(struct nvme_id_uuid_list_entry *entry1, struct nvme_id_uuid_list_entry *entry2); diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index 1f99a64..5568c80 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -54,7 +54,7 @@ static int show_ymtc_smart_log(struct nvme_dev *dev, __u32 nsid, /* Table Title */ printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", dev->name, nsid); - /* Clumn Name*/ + /* Column Name*/ printf("key normalized raw\n"); /* 00 SI_VD_PROGRAM_FAIL */ get_ymtc_smart_info(smart, SI_VD_PROGRAM_FAIL, nm, raw); diff --git a/plugins/ymtc/ymtc-utils.h b/plugins/ymtc/ymtc-utils.h index 39f79cb..e9a3572 100644 --- a/plugins/ymtc/ymtc-utils.h +++ b/plugins/ymtc/ymtc-utils.h @@ -34,7 +34,7 @@ typedef unsigned char u8; #define SI_VD_THERMAL_THROTTLE_TIME_ID 0xEB #define SI_VD_FLASH_MEDIA_ERROR_ID 0xED -/* Addtional smart internal ID */ +/* Additional smart internal ID */ typedef enum { /* smart attr following intel */ diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 5b9d013..a7a3766 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -12,6 +12,7 @@ #include "nvme.h" #include "libnvme.h" #include "nvme-print.h" +#include "util/cleanup.h" #define CREATE_CMD #include "zns.h" @@ -135,9 +136,9 @@ static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *pl if (err) return errno; - err = flags = validate_output_format(cfg.output_format); - if (flags < 0) - goto close_fd; + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; err = nvme_zns_identify_ctrl(dev_fd(dev), &ctrl); if (!err) @@ -146,7 +147,7 @@ static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *pl nvme_show_status(err); else perror("zns identify controller"); -close_fd: +close_dev: dev_close(dev); return err; } @@ -188,9 +189,9 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug if (err) return errno; - flags = validate_output_format(cfg.output_format); - if (flags < 0) - goto close_fd; + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; if (cfg.vendor_specific) flags |= VS; if (cfg.human_readable) @@ -200,14 +201,14 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); if (err < 0) { perror("get-namespace-id"); - goto close_fd; + goto close_dev; } } err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id, &id_ns); if (err) { nvme_show_status(err); - goto close_fd; + goto close_dev; } err = nvme_zns_identify_ns(dev_fd(dev), cfg.namespace_id, &ns); @@ -217,7 +218,7 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug nvme_show_status(err); else perror("zns identify namespace"); -close_fd: +close_dev: dev_close(dev); return err; } @@ -560,7 +561,7 @@ static int set_zone_desc(int argc, char **argv, struct command *cmd, struct plug const char *desc = "Set Zone Descriptor Extension\n"; const char *zslba = "starting LBA of the zone for this command"; const char *zrwaa = "Allocate Zone Random Write Area to zone"; - const char *data = "optional file for zone extention data (default stdin)"; + const char *data = "optional file for zone extension data (default stdin)"; const char *timeout = "timeout value, in milliseconds"; int ffd = STDIN_FILENO, err; @@ -603,7 +604,7 @@ static int set_zone_desc(int argc, char **argv, struct command *cmd, struct plug if (!data_len || data_len < 0) { fprintf(stderr, - "zone format does not provide descriptor extention\n"); + "zone format does not provide descriptor extension\n"); errno = EINVAL; err = -1; goto close_dev; @@ -765,8 +766,8 @@ static int zone_mgmt_recv(int argc, char **argv, struct command *cmd, struct plu if (err) return errno; - flags = validate_output_format(cfg.output_format); - if (flags < 0) + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) goto close_dev; if (!cfg.namespace_id) { @@ -833,20 +834,20 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi int zdes = 0, err = -1; struct nvme_dev *dev; __u32 report_size; - bool huge = false; struct nvme_zone_report *report, *buff; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; unsigned int nr_zones_chunks = 1024, /* 1024 entries * 64 bytes per entry = 64k byte transfer */ nr_zones_retrieved = 0, nr_zones, - offset, log_len; + __u64 offset; int total_nr_zones = 0; struct nvme_zns_id_ns id_zns; struct nvme_id_ns id_ns; uint8_t lbaf; __le64 zsze; - struct json_object *zone_list = 0; + struct json_object *zone_list = NULL; struct config { char *output_format; @@ -880,8 +881,8 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi if (err) return errno; - flags = validate_output_format(cfg.output_format); - if (flags < 0) + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) goto close_dev; if (cfg.verbose) flags |= VERBOSE; @@ -949,7 +950,7 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes)); report_size = log_len; - report = nvme_alloc(report_size, &huge); + report = nvme_alloc_huge(report_size, &mh); if (!report) { perror("alloc"); err = -ENOMEM; @@ -957,10 +958,8 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi } offset = cfg.zslba; - if (flags & JSON) - zone_list = json_create_array(); - else - printf("nr_zones: %"PRIu64"\n", (uint64_t)le64_to_cpu(total_nr_zones)); + + nvme_zns_start_zone_list(total_nr_zones, &zone_list, flags); while (nr_zones_retrieved < nr_zones) { if (nr_zones_retrieved >= nr_zones) @@ -989,15 +988,7 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi offset = le64_to_cpu(report->entries[nr_zones_chunks-1].zslba) + zsze; } - if (flags & JSON) { - struct print_ops *ops; - - ops = nvme_get_json_print_ops(flags); - if (ops) - ops->zns_finish_zone_list(total_nr_zones, zone_list); - } - - nvme_free(report, huge); + nvme_zns_finish_zone_list(total_nr_zones, zone_list, flags); free_buff: free(buff); @@ -1256,15 +1247,15 @@ static int changed_zone_list(int argc, char **argv, struct command *cmd, struct if (err) return errno; - flags = validate_output_format(cfg.output_format); - if (flags < 0) - goto close_fd; + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; if (!cfg.namespace_id) { err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); if (err < 0) { perror("get-namespace-id"); - goto close_fd; + goto close_dev; } } @@ -1277,7 +1268,7 @@ static int changed_zone_list(int argc, char **argv, struct command *cmd, struct else perror("zns changed-zone-list"); -close_fd: +close_dev: dev_close(dev); return err; } diff --git a/plugins/zns/zns.h b/plugins/zns/zns.h index 77bfdd6..43c4ecd 100644 --- a/plugins/zns/zns.h +++ b/plugins/zns/zns.h @@ -15,7 +15,7 @@ PLUGIN(NAME("zns", "Zoned Namespace Command Set", NVME_VERSION), ENTRY("report-zones", "Report zones associated to a Zoned Namespace", report_zones) ENTRY("reset-zone", "Reset one or more zones", reset_zone) ENTRY("close-zone", "Close one or more zones", close_zone) - ENTRY("finish-zone", "Finishe one or more zones", finish_zone) + ENTRY("finish-zone", "Finish one or more zones", finish_zone) ENTRY("open-zone", "Open one or more zones", open_zone) ENTRY("offline-zone", "Offline one or more zones", offline_zone) ENTRY("set-zone-desc", "Attach zone descriptor extension data to a zone", set_zone_desc) |