diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-07-02 20:49:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-07-02 20:49:35 +0000 |
commit | f2c543b4ccad3b9f8871d952cddf66b3b438595b (patch) | |
tree | c3c363d1cc72514221685c42a79a19b320114acc /plugins | |
parent | Adding debian version 1.12-8. (diff) | |
download | nvme-cli-f2c543b4ccad3b9f8871d952cddf66b3b438595b.tar.xz nvme-cli-f2c543b4ccad3b9f8871d952cddf66b3b438595b.zip |
Merging upstream version 1.14.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
29 files changed, 7819 insertions, 1867 deletions
diff --git a/plugins/amzn/amzn-nvme.c b/plugins/amzn/amzn-nvme.c new file mode 100644 index 0000000..47a2d82 --- /dev/null +++ b/plugins/amzn/amzn-nvme.c @@ -0,0 +1,59 @@ +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include "linux/nvme_ioctl.h" + +#include "common.h" +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "plugin.h" + +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include "amzn-nvme.h" + +struct nvme_vu_id_ctrl_field { + __u8 bdev[32]; + __u8 reserved0[992]; +}; + +static void json_amzn_id_ctrl(struct nvme_vu_id_ctrl_field *id, + char *bdev, + struct json_object *root) +{ + json_object_add_value_string(root, "bdev", bdev); +} + +static void amzn_id_ctrl(__u8 *vs, struct json_object *root) +{ + struct nvme_vu_id_ctrl_field* id = (struct nvme_vu_id_ctrl_field *)vs; + + char bdev[32] = { 0 }; + + int len = 0; + while (len < 31) { + if (id->bdev[++len] == ' ') { + break; + } + } + snprintf(bdev, len+1, "%s", id->bdev); + + if (root) { + json_amzn_id_ctrl(id, bdev, root); + return; + } + + printf("bdev : %s\n", bdev); +} + +static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return __id_ctrl(argc, argv, cmd, plugin, amzn_id_ctrl); +} diff --git a/plugins/amzn/amzn-nvme.h b/plugins/amzn/amzn-nvme.h new file mode 100644 index 0000000..9b8d797 --- /dev/null +++ b/plugins/amzn/amzn-nvme.h @@ -0,0 +1,17 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/amzn/amzn-nvme + +#if !defined(AMZN_NVME) || defined(CMD_HEADER_MULTI_READ) +#define AMZN_NVME + +#include "cmd.h" + +PLUGIN(NAME("amzn", "Amazon vendor specific extensions"), + COMMAND_LIST( + ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/dera/dera-nvme.c b/plugins/dera/dera-nvme.c index dbb40cc..be78930 100644 --- a/plugins/dera/dera-nvme.c +++ b/plugins/dera/dera-nvme.c @@ -131,7 +131,8 @@ static int get_status(int argc, char **argv, struct command *cmd, struct plugin if (fd < 0) return fd; - err = nvme_get_log(fd, 0xffffffff, 0xc0, false, sizeof(log), &log); + err = nvme_get_log(fd, 0xffffffff, 0xc0, false, NVME_NO_LOG_LSP, + sizeof(log), &log); if (err) { goto exit; } diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 482ea0d..b39d861 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -32,7 +32,6 @@ #include "nvme-print.h" #include "nvme-ioctl.h" #include "plugin.h" -#include "json.h" #include "argconfig.h" #include "suffix.h" @@ -143,7 +142,7 @@ static void huawei_json_print_list_items(struct huawei_list_item *list_items, unsigned len) { struct json_object *root; - struct json_array *devices; + struct json_object *devices; struct json_object *device_attrs; char formatter[128] = { 0 }; int index, i = 0; diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index 8217c46..27b065d 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -11,7 +11,6 @@ #include "nvme.h" #include "nvme-print.h" #include "nvme-ioctl.h" -#include "json.h" #include "plugin.h" #include "argconfig.h" @@ -54,6 +53,13 @@ struct nvme_additional_smart_log { struct nvme_additional_smart_log_item pll_lock_loss_cnt; struct nvme_additional_smart_log_item nand_bytes_written; struct nvme_additional_smart_log_item host_bytes_written; + struct nvme_additional_smart_log_item host_ctx_wear_used; + struct nvme_additional_smart_log_item perf_stat_indicator; + struct nvme_additional_smart_log_item re_alloc_sectr_cnt; + struct nvme_additional_smart_log_item soft_ecc_err_rate; + struct nvme_additional_smart_log_item unexp_power_loss; + struct nvme_additional_smart_log_item media_bytes_read; + struct nvme_additional_smart_log_item avail_fw_downgrades; }; struct nvme_vu_id_ctrl_field { /* CDR MR5 */ @@ -218,60 +224,118 @@ static void show_intel_smart_log_jsn(struct nvme_additional_smart_log *smart, json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->host_bytes_written.raw)); json_object_add_value_object(dev_stats, "host_bytes_written", entry_stats); + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->host_ctx_wear_used.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->host_ctx_wear_used.raw)); + json_object_add_value_object(dev_stats, "host_ctx_wear_used", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->perf_stat_indicator.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->perf_stat_indicator.raw)); + json_object_add_value_object(dev_stats, "perf_stat_indicator", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->re_alloc_sectr_cnt.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->re_alloc_sectr_cnt.raw)); + json_object_add_value_object(dev_stats, "re_alloc_sectr_cnt", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->soft_ecc_err_rate.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->soft_ecc_err_rate.raw)); + json_object_add_value_object(dev_stats, "soft_ecc_err_rate", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->unexp_power_loss.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->unexp_power_loss.raw)); + json_object_add_value_object(dev_stats, "unexp_power_loss", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->media_bytes_read.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->media_bytes_read.raw)); + json_object_add_value_object(dev_stats, "media_bytes_read", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->avail_fw_downgrades.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->avail_fw_downgrades.raw)); + json_object_add_value_object(dev_stats, "avail_fw_downgrades", entry_stats); + json_object_add_value_object(root, "Device stats", dev_stats); json_print_object(root, NULL); json_free_object(root); } +static char *id_to_key(__u8 id) +{ + switch (id) { + case 0xAB: + return "program_fail_count"; + case 0xAC: + return "erase_fail_count"; + case 0xAD: + return "wear_leveling_count"; + case 0xB8: + return "e2e_error_detect_count"; + case 0xC7: + return "crc_error_count"; + case 0xE2: + return "media_wear_percentage"; + case 0xE3: + return "host_reads"; + case 0xE4: + return "timed_work_load"; + case 0xEA: + return "thermal_throttle_status"; + case 0xF0: + return "retry_buff_overflow_count"; + case 0xF3: + return "pll_lock_loss_counter"; + case 0xF4: + return "nand_bytes_written"; + case 0xF5: + return "host_bytes_written"; + case 0xF6: + return "host_context_wear_used"; + case 0xF7: + return "performance_status_indicator"; + case 0xF8: + return "media_bytes_read"; + case 0xF9: + return "available_fw_downgrades"; + case 0x05: + return "re-allocated_sector_count"; + case 0x0D: + return "soft_ecc_error_rate"; + case 0xAE: + return "unexpected_power_loss"; + default: + return "Invalid ID"; + } +} + +static void print_intel_smart_log_items(struct nvme_additional_smart_log_item *item) +{ + if (!item->key) + return; + + printf("%#x %-45s %3d %"PRIu64"\n", + item->key, id_to_key(item->key), + item->norm, int48_to_long(item->raw)); +} + static void show_intel_smart_log(struct nvme_additional_smart_log *smart, unsigned int nsid, const char *devname) { + struct nvme_additional_smart_log_item *iter = &smart->program_fail_cnt; + int num_items = sizeof(struct nvme_additional_smart_log) / + sizeof(struct nvme_additional_smart_log_item); + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid); - printf("key normalized raw\n"); - printf("program_fail_count : %3d%% %"PRIu64"\n", - smart->program_fail_cnt.norm, - int48_to_long(smart->program_fail_cnt.raw)); - printf("erase_fail_count : %3d%% %"PRIu64"\n", - smart->erase_fail_cnt.norm, - int48_to_long(smart->erase_fail_cnt.raw)); - printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", - smart->wear_leveling_cnt.norm, - le16_to_cpu(smart->wear_leveling_cnt.wear_level.min), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.max), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg)); - printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", - smart->e2e_err_cnt.norm, - int48_to_long(smart->e2e_err_cnt.raw)); - printf("crc_error_count : %3d%% %"PRIu64"\n", - smart->crc_err_cnt.norm, - int48_to_long(smart->crc_err_cnt.raw)); - printf("timed_workload_media_wear : %3d%% %.3f%%\n", - smart->timed_workload_media_wear.norm, - ((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); - printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n", - smart->timed_workload_host_reads.norm, - int48_to_long(smart->timed_workload_host_reads.raw)); - printf("timed_workload_timer : %3d%% %"PRIu64" min\n", - smart->timed_workload_timer.norm, - int48_to_long(smart->timed_workload_timer.raw)); - printf("thermal_throttle_status : %3d%% %u%%, cnt: %u\n", - smart->thermal_throttle_status.norm, - smart->thermal_throttle_status.thermal_throttle.pct, - smart->thermal_throttle_status.thermal_throttle.count); - printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n", - smart->retry_buffer_overflow_cnt.norm, - int48_to_long(smart->retry_buffer_overflow_cnt.raw)); - printf("pll_lock_loss_count : %3d%% %"PRIu64"\n", - smart->pll_lock_loss_cnt.norm, - int48_to_long(smart->pll_lock_loss_cnt.raw)); - printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->nand_bytes_written.norm, - int48_to_long(smart->nand_bytes_written.raw)); - printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->host_bytes_written.norm, - int48_to_long(smart->host_bytes_written.raw)); + printf("ID KEY Normalized Raw\n"); + + for (int i = 0; i < num_items; i++, iter++) + print_intel_smart_log_items(iter); } static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -307,7 +371,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, return fd; err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, - sizeof(smart_log), &smart_log); + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); if (!err) { if (cfg.json) show_intel_smart_log_jsn(&smart_log, cfg.namespace_id, devicename); @@ -347,7 +411,7 @@ static int get_market_log(int argc, char **argv, struct command *cmd, struct plu return fd; err = nvme_get_log(fd, NVME_NSID_ALL, 0xdd, false, - sizeof(log), log); + NVME_NO_LOG_LSP, sizeof(log), log); if (!err) { if (!cfg.raw_binary) printf("Intel Marketing Name Log:\n%s\n", log); @@ -409,7 +473,7 @@ static int get_temp_stats_log(int argc, char **argv, struct command *cmd, struct return fd; err = nvme_get_log(fd, NVME_NSID_ALL, 0xc5, false, - sizeof(stats), &stats); + NVME_NO_LOG_LSP, sizeof(stats), &stats); if (!err) { if (!cfg.raw_binary) show_temp_stats(&stats); @@ -427,6 +491,28 @@ struct intel_lat_stats { __u32 data[1216]; }; +struct __attribute__((__packed__)) optane_lat_stats { + __u16 maj; + __u16 min; + __u64 data[9]; +}; + +#define MEDIA_MAJOR_IDX 0 +#define MEDIA_MINOR_IDX 1 +#define MEDIA_MAX_LEN 2 +#define OPTANE_V1000_BUCKET_LEN 8 + +static struct intel_lat_stats stats; +static struct optane_lat_stats v1000_stats; + +struct v1000_thresholds { + __u32 read[OPTANE_V1000_BUCKET_LEN]; + __u32 write[OPTANE_V1000_BUCKET_LEN]; +}; + +static struct v1000_thresholds v1000_bucket; +static __u16 media_version[MEDIA_MAX_LEN] = {0}; + enum FormatUnit { US, MS, @@ -536,6 +622,28 @@ static void show_lat_stats_bucket(struct intel_lat_stats *stats, printf("%-*d\n", COL_WIDTH, stats->data[i]); } +static void show_optane_lat_stats_bucket(struct optane_lat_stats *stats, + __u32 lower_us, enum inf_bound_type start_type, + __u32 upper_us, enum inf_bound_type end_type, int i) +{ + enum FormatUnit fu = S; + char buffer[BUFSIZE]; + + init_buffer(buffer, BUFSIZE); + printf("%-*d", COL_WIDTH, i); + + fu = get_seconds_magnitude(lower_us); + set_unit_string(buffer, lower_us, fu, start_type); + printf("%-*s", COL_WIDTH, buffer); + + fu = get_seconds_magnitude(upper_us); + set_unit_string(buffer, upper_us, fu, end_type); + printf("%-*s", COL_WIDTH, buffer); + + printf("%-*lu\n", COL_WIDTH, (long unsigned int)stats->data[i]); +} + + static void show_lat_stats_linear(struct intel_lat_stats *stats, __u32 start_offset, __u32 end_offset, __u32 bytes_per, __u32 us_step, bool nonzero_print) @@ -622,6 +730,31 @@ static void json_add_bucket(struct intel_lat_stats *stats, json_object_add_value_int(bucket, "value", val); } +static void json_add_bucket_optane(struct json_object *bucket_list, __u32 id, + __u32 lower_us, enum inf_bound_type start_type, + __u32 upper_us, enum inf_bound_type end_type, __u32 val) +{ + char buffer[BUFSIZE]; + struct json_object *bucket = json_create_object(); + + init_buffer(buffer, BUFSIZE); + + json_object_add_value_object(bucket_list, + "bucket", bucket); + json_object_add_value_int(bucket, "id", id); + + set_unit_string(buffer, lower_us, + get_seconds_magnitude(lower_us), start_type); + json_object_add_value_string(bucket, "start", buffer); + + set_unit_string(buffer, upper_us, + get_seconds_magnitude(upper_us), end_type); + json_object_add_value_string(bucket, "end", buffer); + + json_object_add_value_uint(bucket, "value", val); + +} + static void json_lat_stats_linear(struct intel_lat_stats *stats, struct json_object *bucket_list, __u32 start_offset, __u32 end_offset, __u32 bytes_per, @@ -714,32 +847,121 @@ static void show_lat_stats_4_0(struct intel_lat_stats *stats) } } -static void json_lat_stats(struct intel_lat_stats *stats, int write) +static void jason_lat_stats_v1000_0(struct optane_lat_stats *stats, int write) +{ + int i; + struct json_object *root = json_create_object(); + struct json_object *bucket_list = json_create_object(); + + lat_stats_make_json_root(root, bucket_list, write); + + if (write) { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + json_add_bucket_optane(bucket_list, i, + v1000_bucket.write[i], NOINF, + v1000_bucket.write[i + 1] - 1, + NOINF, + stats->data[i]); + + json_add_bucket_optane(bucket_list, + OPTANE_V1000_BUCKET_LEN - 1, + v1000_bucket.write[i], + NOINF, + v1000_bucket.write[i], + POSINF, stats->data[i]); + } else { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + json_add_bucket_optane(bucket_list, i, + v1000_bucket.read[i], NOINF, + v1000_bucket.read[i + 1] - 1, + NOINF, + stats->data[i]); + + json_add_bucket_optane(bucket_list, + OPTANE_V1000_BUCKET_LEN - 1, + v1000_bucket.read[i], + NOINF, + v1000_bucket.read[i], + POSINF, stats->data[i]); + } + + struct json_object *subroot = json_create_object(); + json_object_add_value_object(root, "Average latency since last reset", subroot); + + json_object_add_value_uint(subroot, "value in us", stats->data[8]); + + json_print_object(root, NULL); + json_free_object(root); + +} + +static void show_lat_stats_v1000_0(struct optane_lat_stats *stats, int write) +{ + int i; + if (write) { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + show_optane_lat_stats_bucket(stats, + v1000_bucket.write[i], + NOINF, + v1000_bucket.write[i + 1] -1, + NOINF, i); + + show_optane_lat_stats_bucket(stats, v1000_bucket.write[i], + NOINF, v1000_bucket.write[i], + POSINF, i); + } else { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + show_optane_lat_stats_bucket(stats, + v1000_bucket.read[i], + NOINF, + v1000_bucket.read[i + 1] -1, + NOINF, i); + + show_optane_lat_stats_bucket(stats, v1000_bucket.read[i], + NOINF, v1000_bucket.read[i], + POSINF, i); + } + + printf("Average latency since last reset: %lu us\n", (long unsigned int)stats->data[8]); + +} + +static void json_lat_stats(int write) { - switch (stats->maj) { + switch (media_version[MEDIA_MAJOR_IDX]) { case 3: - json_lat_stats_3_0(stats, write); + json_lat_stats_3_0(&stats, write); break; case 4: - switch (stats->min) { + switch (media_version[MEDIA_MINOR_IDX]) { case 0: case 1: case 2: case 3: case 4: case 5: - json_lat_stats_4_0(stats, write); + json_lat_stats_4_0(&stats, write); break; default: - printf(("Unsupported minor revision (%u.%u)\n" - "Defaulting to format for rev4.0"), - stats->maj, stats->min); + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); + break; + } + break; + case 1000: + switch (media_version[MEDIA_MINOR_IDX]) { + case 0: + jason_lat_stats_v1000_0(&v1000_stats, write); + break; + default: + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); break; } break; default: printf("Unsupported revision (%u.%u)\n", - stats->maj, stats->min); + stats.maj, stats.min); break; } printf("\n"); @@ -752,69 +974,79 @@ static void print_dash_separator(int count) putchar('\n'); } -static void show_lat_stats(struct intel_lat_stats *stats, int write) +static void show_lat_stats(int write) { static const int separator_length = 50; printf("Intel IO %s Command Latency Statistics\n", write ? "Write" : "Read"); printf("Major Revision : %u\nMinor Revision : %u\n", - stats->maj, stats->min); + media_version[MEDIA_MAJOR_IDX], media_version[MEDIA_MINOR_IDX]); print_dash_separator(separator_length); printf("%-12s%-12s%-12s%-20s\n", "Bucket", "Start", "End", "Value"); print_dash_separator(separator_length); - switch (stats->maj) { + switch (media_version[MEDIA_MAJOR_IDX]) { case 3: - show_lat_stats_3_0(stats); + show_lat_stats_3_0(&stats); break; case 4: - switch (stats->min) { + switch (media_version[MEDIA_MINOR_IDX]) { case 0: case 1: case 2: case 3: case 4: case 5: - show_lat_stats_4_0(stats); + show_lat_stats_4_0(&stats); break; default: - printf(("Unsupported minor revision (%u.%u)\n" - "Defaulting to format for rev4.0"), - stats->maj, stats->min); + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); + break; + } + break; + case 1000: + switch (media_version[MEDIA_MINOR_IDX]) { + case 0: + show_lat_stats_v1000_0(&v1000_stats, write); + break; + default: + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); break; } break; default: printf("Unsupported revision (%u.%u)\n", - stats->maj, stats->min); + stats.maj, stats.min); break; } } static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - struct intel_lat_stats stats; - enum nvme_print_flags flags; + int err, fd; const char *desc = "Get Intel Latency Statistics log and show it."; - const char *raw = "dump output in binary format"; + const char *raw = "Dump output in binary format"; + const char *json= "Dump output in json format"; const char *write = "Get write statistics (read default)"; + struct config { - char *output_format; int raw_binary; + int json; int write; }; struct config cfg = { - .output_format = "normal", }; OPT_ARGS(opts) = { - OPT_FLAG("write", 'w', &cfg.write, write), - OPT_FMT("output-format", 'o', &cfg.output_format, "Output format: normal|json|binary"), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG("json", 'j', &cfg.json, json), OPT_END() }; @@ -822,26 +1054,54 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct if (fd < 0) return fd; - err = flags = validate_output_format(cfg.output_format); - if (flags < 0) + /* Query maj and minor version first */ + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, + false, NVME_NO_LOG_LSP, sizeof(media_version), + media_version); + if (err) goto close_fd; - if (cfg.raw_binary) - flags = BINARY; + if (media_version[0] == 1000) { + __u32 thresholds[OPTANE_V1000_BUCKET_LEN] = {0}; + __u32 result; + + err = nvme_get_feature(fd, 0, 0xf7, 0, cfg.write ? 0x1 : 0x0, + sizeof(thresholds), thresholds, &result); + if (err) { + fprintf(stderr, "Quering thresholds failed. NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + goto close_fd; + } + + /* Update bucket thresholds to be printed */ + if (cfg.write) { + for (int i = 0; i < OPTANE_V1000_BUCKET_LEN; i++) + v1000_bucket.write[i] = thresholds[i]; + } else { + for (int i = 0; i < OPTANE_V1000_BUCKET_LEN; i++) + v1000_bucket.read[i] = thresholds[i]; + + } + + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, + false, NVME_NO_LOG_LSP, sizeof(v1000_stats), + &v1000_stats); + } else { + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, + false, NVME_NO_LOG_LSP, sizeof(stats), + &stats); + } - err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, - false, sizeof(stats), &stats); if (!err) { - if (flags & JSON) - json_lat_stats(&stats, cfg.write); - else if (flags & BINARY) - d_raw((unsigned char *)&stats, sizeof(stats)); + if (cfg.json) + json_lat_stats(cfg.write); + else if (!cfg.raw_binary) + show_lat_stats(cfg.write); else - show_lat_stats(&stats, cfg.write); + d_raw((unsigned char *)&stats, sizeof(stats)); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - close_fd: close(fd); return err; @@ -1314,3 +1574,84 @@ static int enable_lat_stats_tracking(int argc, char **argv, } return fd; } + +static int set_lat_stats_thresholds(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + int err, fd, num; + const char *desc = "Write Intel Bucket Thresholds for Latency Statistics Tracking"; + const char *bucket_thresholds = "Bucket Threshold List, comma separated list: 0, 10, 20 ..."; + const char *write = "Set write bucket Thresholds for latency tracking (read default)"; + + const __u32 nsid = 0; + const __u8 fid = 0xf7; + const __u32 cdw12 = 0x0; + const __u32 save = 0; + __u32 result; + + struct config { + int write; + char *bucket_thresholds; + }; + + struct config cfg = { + .write = 0, + .bucket_thresholds = "", + }; + + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_LIST("bucket-thresholds", 't', &cfg.bucket_thresholds, + bucket_thresholds), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + return fd; + + /* Query maj and minor version first to figure out the amount of + * valid buckets a user is allowed to modify. Read or write doesn't + * matter + */ + err = nvme_get_log(fd, NVME_NSID_ALL, 0xc2, + false, NVME_NO_LOG_LSP, sizeof(media_version), + media_version); + if (err) { + fprintf(stderr, "Querying media version failed. NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + goto close_fd; + } + + if (media_version[0] == 1000) { + int thresholds[OPTANE_V1000_BUCKET_LEN] = {0}; + num = argconfig_parse_comma_sep_array(cfg.bucket_thresholds, + thresholds, + sizeof(thresholds)); + if (num == -1) { + fprintf(stderr, "ERROR: Bucket list is malformed\n"); + goto close_fd; + + } + + err = nvme_set_feature(fd, nsid, fid, cfg.write ? 0x1 : 0x0, + cdw12, save, OPTANE_V1000_BUCKET_LEN, + thresholds, &result); + + if (err > 0) { + fprintf(stderr, "NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + } else if (err < 0) { + perror("Enable latency tracking"); + fprintf(stderr, "Command failed while parsing.\n"); + } + } else { + fprintf(stderr, "Unsupported command\n"); + } + +close_fd: + close(fd); + return err; +} + diff --git a/plugins/intel/intel-nvme.h b/plugins/intel/intel-nvme.h index 335e901..b119004 100644 --- a/plugins/intel/intel-nvme.h +++ b/plugins/intel/intel-nvme.h @@ -11,6 +11,7 @@ PLUGIN(NAME("intel", "Intel vendor specific extensions"), ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) ENTRY("internal-log", "Retrieve Intel internal firmware log, save it", get_internal_log) ENTRY("lat-stats", "Retrieve Intel IO Latency Statistics log, show it", get_lat_stats_log) + ENTRY("set-bucket-thresholds", "Set Latency Stats Bucket Values, save it", set_lat_stats_thresholds) ENTRY("lat-stats-tracking", "Enable and disable Latency Statistics logging.", enable_lat_stats_tracking) ENTRY("market-name", "Retrieve Intel Marketing Name log, show it", get_market_log) ENTRY("smart-log-add", "Retrieve Intel SMART Log, show it", get_additional_smart_log) diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index c75f49c..d330835 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -3,6 +3,7 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <time.h> #include "linux/nvme_ioctl.h" @@ -19,9 +20,23 @@ #include "memblaze-utils.h" enum { - MB_FEAT_POWER_MGMT = 0xc6, + // feature id + MB_FEAT_POWER_MGMT = 0x02, + MB_FEAT_HIGH_LATENCY = 0xE1, + // log id + GLP_ID_VU_GET_READ_LATENCY_HISTOGRAM = 0xC1, + GLP_ID_VU_GET_WRITE_LATENCY_HISTOGRAM = 0xC2, + GLP_ID_VU_GET_HIGH_LATENCY_LOG = 0xC3, + MB_FEAT_CLEAR_ERRORLOG = 0xF7, }; +#define LOG_PAGE_SIZE (0x1000) +#define DO_PRINT_FLAG (1) +#define NOT_PRINT_FLAG (0) +#define FID_C1_LOG_FILENAME "log_c1.csv" +#define FID_C2_LOG_FILENAME "log_c2.csv" +#define FID_C3_LOG_FILENAME "log_c3.csv" + /* * Return -1 if @fw1 < @fw2 * Return 0 if @fw1 == @fw2 @@ -53,18 +68,26 @@ static int compare_fw_version(const char *fw1, const char *fw2) #define MEMBLAZE_FORMAT (0) #define INTEL_FORMAT (1) -// 2.83 = raisin -#define IS_RAISIN(str) (!strcmp(str, "2.83")) // 2.13 = papaya #define IS_PAPAYA(str) (!strcmp(str, "2.13")) -#define STR_VER_SIZE 5 +// 2.83 = raisin +#define IS_RAISIN(str) (!strcmp(str, "2.83")) +// 2.94 = kumquat +#define IS_KUMQUAT(str) (!strcmp(str, "2.94")) +// 0.60 = loquat +#define IS_LOQUAT(str) (!strcmp(str, "0.60")) + +#define STR_VER_SIZE (5) int getlogpage_format_type(char *fw_ver) { char fw_ver_local[STR_VER_SIZE]; strncpy(fw_ver_local, fw_ver, STR_VER_SIZE); *(fw_ver_local + STR_VER_SIZE - 1) = '\0'; - if ( IS_RAISIN(fw_ver_local) ) + if ( IS_RAISIN(fw_ver_local) + || IS_KUMQUAT(fw_ver_local) + || IS_LOQUAT(fw_ver_local) + ) { return INTEL_FORMAT; } @@ -128,7 +151,7 @@ static __u64 raw_2_u64(const __u8 *buf, size_t len) #define STR17_04 ", min: " #define STR17_05 ", curr: " #define STR18_01 "power_loss_protection" -#define STR19_01 "read_fail" +#define STR19_01 "read_fail_count" #define STR20_01 "thermal_throttle_time" #define STR21_01 "flash_media_error" @@ -380,7 +403,42 @@ static int show_memblaze_smart_log(int fd, __u32 nsid, const char *devname, return err; } -static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +int parse_params(char *str, int number, ...) +{ + va_list argp; + int *param; + char *c; + int value; + + va_start(argp, number); + + while (number > 0) { + c = strtok(str, ","); + if ( c == NULL) { + printf("No enough parameters. abort...\n"); + exit(EINVAL); + } + + if (isalnum(*c) == 0) { + printf("%s is not a valid number\n", c); + return 1; + } + value = atoi(c); + param = va_arg(argp, int *); + *param = value; + + if (str) { + str = strchr(str, ','); + if (str) { str++; } + } + number--; + } + va_end(argp); + + return 0; +} + +static int mb_get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { struct nvme_memblaze_smart_log smart_log; int err, fd; @@ -408,7 +466,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, return fd; err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, - sizeof(smart_log), &smart_log); + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); if (!err) { if (!cfg.raw_binary) err = show_memblaze_smart_log(fd, cfg.namespace_id, devicename, &smart_log); @@ -423,213 +481,262 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, static char *mb_feature_to_string(int feature) { - switch (feature) { - case MB_FEAT_POWER_MGMT: return "Memblaze power management"; - default: return "Unknown"; - } + switch (feature) { + case MB_FEAT_POWER_MGMT: return "Memblaze power management"; + case MB_FEAT_HIGH_LATENCY: return "Memblaze high latency log"; + case MB_FEAT_CLEAR_ERRORLOG: return "Memblaze clear error log"; + default: return "Unknown"; + } } -static int get_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_get_powermanager_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - const char *desc = "Read operating parameters of the "\ - "specified controller. Operating parameters are grouped "\ - "and identified by Feature Identifiers; each Feature "\ - "Identifier contains one or more attributes that may affect "\ - "behaviour of the feature. Each Feature has three possible "\ - "settings: default, saveable, and current. If a Feature is "\ - "saveable, it may be modified by set-feature. Default values "\ - "are vendor-specific and not changeable. Use set-feature to "\ - "change saveable Features.\n\n"\ - "Available additional feature id:\n"\ - "0xc6: Memblaze power management\n"\ - " (value 0 - 25w, 1 - 20w, 2 - 15w)"; - const char *raw = "show feature in binary format"; - const char *namespace_id = "identifier of desired namespace"; - const char *feature_id = "hexadecimal feature name"; - const char *sel = "[0-3]: curr./default/saved/supp."; - const char *data_len = "buffer len (if) data is returned"; - const char *cdw11 = "dword 11 for interrupt vector config"; - const char *human_readable = "show infos in readable format"; - int err, fd; - __u32 result; - void *buf = NULL; - - struct config { - __u32 namespace_id; - __u32 feature_id; - __u8 sel; - __u32 cdw11; - __u32 data_len; - int raw_binary; - int human_readable; - }; + const char *desc = "Get Memblaze power management ststus\n (value 0 - 25w, 1 - 20w, 2 - 15w)"; + int err, fd; + __u32 result; + __u32 feature_id = MB_FEAT_POWER_MGMT; - struct config cfg = { - .namespace_id = 1, - .feature_id = 0, - .sel = 0, - .cdw11 = 0, - .data_len = 0, - }; + OPT_ARGS(opts) = { + OPT_END() + }; - OPT_ARGS(opts) = { - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_END() - }; + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) - return fd; + err = nvme_get_feature(fd, 0, feature_id, 0, 0, 0, NULL, &result); + if (err < 0) { + perror("get-feature"); + } + if (!err) { + printf("get-feature:0x%02x (%s), %s value: %#08x\n", feature_id, + mb_feature_to_string(feature_id), + nvme_select_to_string(0), result); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + return err; +} - if (cfg.sel > 7) { - fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel); - return EINVAL; - } - if (!cfg.feature_id) { - fprintf(stderr, "feature-id required param\n"); - return EINVAL; - } - if (cfg.data_len) { - if (posix_memalign(&buf, getpagesize(), cfg.data_len)) - exit(ENOMEM); - memset(buf, 0, cfg.data_len); - } +static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Set Memblaze power management status\n (value 0 - 25w, 1 - 20w, 2 - 15w)"; + const char *value = "new value of feature (required)"; + const char *save = "specifies that the controller shall save the attribute"; + int err, fd; + __u32 result; + + struct config { + __u32 feature_id; + __u32 value; + int save; + }; + + struct config cfg = { + .feature_id = MB_FEAT_POWER_MGMT, + .value = 0, + .save = 0, + }; + + OPT_ARGS(opts) = { + OPT_UINT("value", 'v', &cfg.value, value), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + err = nvme_set_feature(fd, 0, cfg.feature_id, cfg.value, 0, cfg.save, 0, NULL, &result); + if (err < 0) { + perror("set-feature"); + } + if (!err) { + printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, + mb_feature_to_string(cfg.feature_id), cfg.value); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11, - cfg.data_len, buf, &result); - if (!err) { - printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id, - mb_feature_to_string(cfg.feature_id), - nvme_select_to_string(cfg.sel), result); - if (cfg.human_readable) - nvme_feature_show_fields(cfg.feature_id, result, buf); - else { - if (buf) { - if (!cfg.raw_binary) - d(buf, cfg.data_len, 16, 1); - else - d_raw(buf, cfg.data_len); - } - } - } else if (err > 0) - fprintf(stderr, "NVMe Status:%s(%x)\n", - nvme_status_to_string(err), err); - if (buf) - free(buf); - return err; + return err; } -static int set_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) +#define P2MIN (1) +#define P2MAX (5000) +#define MB_FEAT_HIGH_LATENCY_VALUE_SHIFT (15) +static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - const char *desc = "Modify the saveable or changeable "\ - "current operating parameters of the controller. Operating "\ - "parameters are grouped and identified by Feature "\ - "Identifiers. Feature settings can be applied to the entire "\ - "controller and all associated namespaces, or to only a few "\ - "namespace(s) associated with the controller. Default values "\ - "for each Feature are vendor-specific and may not be modified."\ - "Use get-feature to determine which Features are supported by "\ - "the controller and are saveable/changeable.\n\n"\ - "Available additional feature id:\n"\ - "0xc6: Memblaze power management\n"\ - " (value 0 - 25w, 1 - 20w, 2 - 15w)"; - const char *namespace_id = "desired namespace"; - const char *feature_id = "hex feature name (required)"; - const char *data_len = "buffer length if data required"; - const char *data = "optional file for feature data (default stdin)"; - const char *value = "new value of feature (required)"; - const char *save = "specifies that the controller shall save the attribute"; - int err, fd; - __u32 result; - void *buf = NULL; - int ffd = STDIN_FILENO; + const char *desc = "Set Memblaze high latency log\n"\ + " input parameter p1,p2\n"\ + " p1 value: 0 is disable, 1 is enable\n"\ + " p2 value: 1 .. 5000 ms"; + const char *param = "input parameters"; + int err, fd; + __u32 result; + int param1 = 0, param2 = 0; + + struct config { + __u32 feature_id; + char * param; + __u32 value; + }; + + struct config cfg = { + .feature_id = MB_FEAT_HIGH_LATENCY, + .param = "0,0", + .value = 0, + }; + + OPT_ARGS(opts) = { + OPT_LIST("param", 'p', &cfg.param, param), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + if (parse_params(cfg.param, 2, ¶m1, ¶m2)) { + printf("setfeature: invalid formats %s\n", cfg.param); + exit(EINVAL); + } + if ((param1 == 1) && (param2 < P2MIN || param2 > P2MAX)) { + printf("setfeature: invalid high io latency threshold %d\n", param2); + exit(EINVAL); + } + cfg.value = (param1 << MB_FEAT_HIGH_LATENCY_VALUE_SHIFT) | param2; - struct config { - char *file; - __u32 namespace_id; - __u32 feature_id; - __u32 value; - __u32 data_len; - int save; - }; + err = nvme_set_feature(fd, 0, cfg.feature_id, cfg.value, 0, 0, 0, NULL, &result); + if (err < 0) { + perror("set-feature"); + } + if (!err) { + printf("set-feature:0x%02X (%s), value:%#08x\n", cfg.feature_id, + mb_feature_to_string(cfg.feature_id), cfg.value); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - struct config cfg = { - .file = "", - .namespace_id = 0, - .feature_id = 0, - .value = 0, - .data_len = 0, - .save = 0, - }; + return err; +} - OPT_ARGS(opts) = { - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'v', &cfg.value, value), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_FILE("data", 'd', &cfg.file, data), - OPT_FLAG("save", 's', &cfg.save, save), - OPT_END() - }; +static int glp_high_latency_show_bar(FILE *fdi, int print) +{ + fPRINT_PARAM1("Memblaze High Latency Log\n"); + fPRINT_PARAM1("---------------------------------------------------------------------------------------------\n"); + fPRINT_PARAM1("Timestamp Type QID CID NSID StartLBA NumLBA Latency\n"); + fPRINT_PARAM1("---------------------------------------------------------------------------------------------\n"); + return 0; +} - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) - return fd; +/* High latency log page definiton + * Total 32 bytes + */ +typedef struct +{ + __u8 port; + __u8 revision; + __u16 rsvd; + __u8 opcode; + __u8 sqe; + __u16 cid; + __u32 nsid; + __u32 latency; + __u64 sLBA; + __u16 numLBA; + __u16 timestampH; + __u32 timestampL; +} log_page_high_latency_t; /* total 32 bytes */ + +static int find_deadbeef(char *buf) +{ + if (((*(buf + 0) & 0xff) == 0xef) && ((*(buf + 1) & 0xff) == 0xbe) && \ + ((*(buf + 2) & 0xff) == 0xad) && ((*(buf + 3) & 0xff) == 0xde)) + { + return 1; + } + return 0; +} - if (!cfg.feature_id) { - fprintf(stderr, "feature-id required param\n"); - return EINVAL; - } +#define TIME_STR_SIZE (44) +static int glp_high_latency(FILE *fdi, char *buf, int buflen, int print) +{ + log_page_high_latency_t *logEntry; + char string[TIME_STR_SIZE]; + int i, entrySize; + __u64 timestamp; + time_t tt = 0; + struct tm *t = NULL; + int millisec = 0; + + if (find_deadbeef(buf)) return 0; + + entrySize = sizeof(log_page_high_latency_t); + for (i = 0; i < buflen; i += entrySize) + { + logEntry = (log_page_high_latency_t *)(buf + i); - if (cfg.data_len) { - if (posix_memalign(&buf, getpagesize(), cfg.data_len)) - exit(ENOMEM); - memset(buf, 0, cfg.data_len); - } + if (logEntry->latency == 0 && logEntry->revision == 0) + { + return 1; + } + + if (0 == logEntry->timestampH) // generate host time string + { + snprintf(string, sizeof(string), "%d", logEntry->timestampL); + } + else // sort + { + timestamp = logEntry->timestampH - 1; + timestamp = timestamp << 32; + timestamp += logEntry->timestampL; + tt = timestamp / 1000; + millisec = timestamp % 1000; + t = gmtime(&tt); + snprintf(string, sizeof(string), "%4d%02d%02d--%02d:%02d:%02d.%03d UTC", + 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, millisec); + } + + fprintf(fdi, "%-32s %-7x %-6x %-6x %-8x %4x%08x %-8x %-d\n", + string, logEntry->opcode, logEntry->sqe, logEntry->cid, logEntry->nsid, + (__u32)(logEntry->sLBA >> 32), (__u32)logEntry->sLBA, logEntry->numLBA, logEntry->latency); + if (print) + { + printf("%-32s %-7x %-6x %-6x %-8x %4x%08x %-8x %-d\n", + string, logEntry->opcode, logEntry->sqe, logEntry->cid, logEntry->nsid, + (__u32)(logEntry->sLBA >> 32), (__u32)logEntry->sLBA, logEntry->numLBA, logEntry->latency); + } + } + return 1; +} - if (buf) { - if (strlen(cfg.file)) { - ffd = open(cfg.file, O_RDONLY); - if (ffd <= 0) { - fprintf(stderr, "no firmware file provided\n"); - err = EINVAL; - goto free; - } - } - if (read(ffd, (void *)buf, cfg.data_len) < 0) { - fprintf(stderr, "failed to read data buffer from input file\n"); - err = EINVAL; - goto free; - } - } +static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Get Memblaze high latency log"; + int err, fd; + char buf[LOG_PAGE_SIZE]; + FILE *fdi = NULL; + + fdi = fopen(FID_C3_LOG_FILENAME, "w+"); + OPT_ARGS(opts) = { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + glp_high_latency_show_bar(fdi, DO_PRINT_FLAG); + err = nvme_get_log(fd, NVME_NSID_ALL, GLP_ID_VU_GET_HIGH_LATENCY_LOG, 0, NVME_NO_LOG_LSP, sizeof(buf), &buf); + + while ( 1) { + if (!glp_high_latency(fdi, buf, LOG_PAGE_SIZE, DO_PRINT_FLAG)) break; + err = nvme_get_log(fd, NVME_NSID_ALL, GLP_ID_VU_GET_HIGH_LATENCY_LOG, 0, NVME_NO_LOG_LSP, sizeof(buf), &buf); + if ( err) { + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + break; + } + } - err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value, - 0, cfg.save, cfg.data_len, buf, &result); - if (err < 0) { - perror("set-feature"); - goto free; - } - if (!err) { - printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, - mb_feature_to_string(cfg.feature_id), cfg.value); - if (buf) - d(buf, cfg.data_len, 16, 1); - } else if (err > 0) - fprintf(stderr, "NVMe Status:%s(%x)\n", - nvme_status_to_string(err), err); - -free: - if (buf) - free(buf); - return err; + if (NULL != fdi) fclose(fdi); + return err; } + static int memblaze_fw_commit(int fd, int select) { struct nvme_admin_cmd cmd = { @@ -641,7 +748,7 @@ static int memblaze_fw_commit(int fd, int select) return nvme_submit_admin_passthru(fd, &cmd); } -static int memblaze_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "This performs a selective firmware download, which allows the user to " @@ -757,3 +864,287 @@ out: return err; } +static void ioLatencyHistogramOutput(FILE *fd, int index, int start, int end, char *unit0, + char *unit1, unsigned int *pHistogram, int print) +{ + int len; + char string[64], subString0[12], subString1[12]; + + len = snprintf(subString0, sizeof(subString0), "%d%s", start, unit0); + if (end != 0x7FFFFFFF) + { + len = snprintf(subString1, sizeof(subString1), "%d%s", end, unit1); + } + else + { + len = snprintf(subString1, sizeof(subString1), "%s", "+INF"); + } + len = snprintf(string, sizeof(string), "%-11d %-11s %-11s %-11u\n", index, subString0, subString1, + pHistogram[index]); + fwrite(string, 1, len, fd); + if (print) + { + printf("%s", string); + } +} + +int io_latency_histogram(char *file, char *buf, int print, int logid) +{ + FILE *fdi = fopen(file, "w+"); + int i, index; + char unit[2][3]; + unsigned int *revision = (unsigned int *)buf; + + if (logid == GLP_ID_VU_GET_READ_LATENCY_HISTOGRAM) + { + fPRINT_PARAM1("Memblaze IO Read Command Latency Histogram\n"); + } + else if (logid == GLP_ID_VU_GET_WRITE_LATENCY_HISTOGRAM) + { + fPRINT_PARAM1("Memblaze IO Write Command Latency Histogram\n"); + } + fPRINT_PARAM2("Major Revision : %d\n", revision[1]); + fPRINT_PARAM2("Minor Revision : %d\n", revision[0]); + buf += 8; + + if (revision[1] == 1 && revision[0] == 0) + { + fPRINT_PARAM1("--------------------------------------------------\n"); + fPRINT_PARAM1("Bucket Start End Value \n"); + fPRINT_PARAM1("--------------------------------------------------\n"); + index = 0; + strcpy(unit[0], "us"); + strcpy(unit[1], "us"); + for (i = 0; i < 32; i++, index++) + { + if (i == 31) + { + strcpy(unit[1], "ms"); + ioLatencyHistogramOutput(fdi, index, i * 32, 1, unit[0], unit[1], (unsigned int *)buf, print); + } + else + { + ioLatencyHistogramOutput(fdi, index, i * 32, (i + 1) * 32, unit[0], unit[1], (unsigned int *)buf, + print); + } + } + + strcpy(unit[0], "ms"); + strcpy(unit[1], "ms"); + for (i = 1; i < 32; i++, index++) + { + ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); + } + + for (i = 1; i < 32; i++, index++) + { + if (i == 31) + { + strcpy(unit[1], "s"); + ioLatencyHistogramOutput(fdi, index, i * 32, 1, unit[0], unit[1], (unsigned int *)buf, print); + } + else + { + ioLatencyHistogramOutput(fdi, index, i * 32, (i + 1) * 32, unit[0], unit[1], (unsigned int *)buf, + print); + } + } + + strcpy(unit[0], "s"); + strcpy(unit[1], "s"); + for (i = 1; i < 4; i++, index++) + { + ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); + } + + ioLatencyHistogramOutput(fdi, index, i, 0x7FFFFFFF, unit[0], unit[1], (unsigned int *)buf, print); + } + else + { + fPRINT_PARAM1("Unsupported io latency histogram revision\n"); + } + + fclose(fdi); + return 1; +} + +static int mb_lat_stats_log_print(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + char stats[LOG_PAGE_SIZE]; + int err = 0; + int fd; + char f1[] = FID_C1_LOG_FILENAME; + char f2[] = FID_C2_LOG_FILENAME; + + const char *desc = "Get Latency Statistics log and show it."; + const char *write = "Get write statistics (read default)"; + + struct config { + int write; + }; + struct config cfg = { + .write = 0, + }; + + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, false, NVME_NO_LOG_LSP, sizeof(stats), &stats); + if (!err) + io_latency_histogram(cfg.write ? f2 : f1, stats, DO_PRINT_FLAG, + cfg.write ? GLP_ID_VU_GET_WRITE_LATENCY_HISTOGRAM : GLP_ID_VU_GET_READ_LATENCY_HISTOGRAM); + else + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + + close(fd); + return err; +} + +#define OP 0xFC +#define FID 0x68 +static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int err, fd; + char *desc = "Clear Memblaze devices error log."; + + //const char *value = "new value of feature (required)"; + //const char *save = "specifies that the controller shall save the attribute"; + __u32 result; + + struct config { + __u32 feature_id; + __u32 value; + int save; + }; + + struct config cfg = { + .feature_id = 0xf7, + .value = 0x534d0001, + .save = 0, + }; + + OPT_ARGS(opts) = { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + + + err = nvme_set_feature(fd, 0, cfg.feature_id, cfg.value, 0, cfg.save, 0, NULL, &result); + if (err < 0) { + perror("set-feature"); + } + if (!err) { + printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, mb_feature_to_string(cfg.feature_id), cfg.value); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); +/* + struct nvme_admin_cmd admin_cmd = { + .opcode = OP, + .cdw10 = FID, + }; + + err = nvme_submit_admin_passthru(fd, &admin_cmd); + + if (!err) { + printf("OP(0x%2X) FID(0x%2X) Clear error log success.\n", OP, FID); + } else { + printf("NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + }; +*/ + return err; +} + +static int mb_set_lat_stats(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + int err, fd; + const char *desc = ( + "Enable/Disable Latency Statistics Tracking.\n" + "No argument prints current status."); + const char *enable_desc = "Enable LST"; + const char *disable_desc = "Disable LST"; + const __u32 nsid = 0; + const __u8 fid = 0xe2; + const __u8 sel = 0; + const __u32 cdw11 = 0x0; + const __u32 cdw12 = 0x0; + const __u32 data_len = 32; + const __u32 save = 0; + __u32 result; + void *buf = NULL; + + struct config { + bool enable, disable; + }; + + struct config cfg = { + .enable = false, + .disable = false, + }; + + const struct argconfig_commandline_options command_line_options[] = { + {"enable", 'e', "", CFG_NONE, &cfg.enable, no_argument, enable_desc}, + {"disable", 'd', "", CFG_NONE, &cfg.disable, no_argument, disable_desc}, + {NULL} + }; + + fd = parse_and_open(argc, argv, desc, command_line_options); + + enum Option { + None = -1, + True = 1, + False = 0, + }; + enum Option option = None; + + if (cfg.enable && cfg.disable) + printf("Cannot enable and disable simultaneously."); + else if (cfg.enable || cfg.disable) + option = cfg.enable; + + if (fd < 0) + return fd; + switch (option) { + case None: + err = nvme_get_feature(fd, nsid, fid, sel, cdw11, data_len, buf, + &result); + if (!err) { + printf( + "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n", + fid, result); + } else { + printf("Could not read feature id 0xE2.\n"); + return err; + } + break; + case True: + case False: + err = nvme_set_feature(fd, nsid, fid, option, cdw12, save, + data_len, buf, &result); + if (err > 0) { + fprintf(stderr, "NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + } else if (err < 0) { + perror("Enable latency tracking"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + printf("Successfully set enable bit for FID (0x%X) to %i.\n", + fid, option); + } + break; + default: + printf("%d not supported.\n", option); + return EINVAL; + } + return fd; +} + diff --git a/plugins/memblaze/memblaze-nvme.h b/plugins/memblaze/memblaze-nvme.h index 8f7024d..043d0a8 100644 --- a/plugins/memblaze/memblaze-nvme.h +++ b/plugins/memblaze/memblaze-nvme.h @@ -14,10 +14,15 @@ PLUGIN(NAME("memblaze", "Memblaze vendor specific extensions"), COMMAND_LIST( - ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", get_additional_smart_log) - ENTRY("get-feature-add", "Get Memblaze feature and show the resulting value", get_additional_feature) - ENTRY("set-feature-add", "Set a Memblaze feature and show the resulting value", set_additional_feature) - ENTRY("select-download", "Selective Firmware Download", memblaze_selective_download) + ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", mb_get_additional_smart_log) + ENTRY("get-pm-status", "Get Memblaze Power Manager Status", mb_get_powermanager_status) + ENTRY("set-pm-status", "Set Memblaze Power Manager Status", mb_set_powermanager_status) + ENTRY("select-download", "Selective Firmware Download", mb_selective_download) + ENTRY("lat-stats", "Enable and disable Latency Statistics logging", mb_set_lat_stats) + ENTRY("lat-stats-print", "Retrieve IO Latency Statistics log, show it", mb_lat_stats_log_print) + 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) ) ); diff --git a/plugins/memblaze/memblaze-utils.h b/plugins/memblaze/memblaze-utils.h index a67ee5e..6fdee39 100644 --- a/plugins/memblaze/memblaze-utils.h +++ b/plugins/memblaze/memblaze-utils.h @@ -160,5 +160,68 @@ struct nvme_p4_smart_log u8 resv[SMART_INFO_NEW_SIZE - sizeof(struct nvme_p4_smart_log_item) * NR_SMART_ITEMS]; }; +// base +#define DD do{ printf("=Memblaze= %s[%d]-%s():\n", __FILE__, __LINE__, __func__); }while(0) +#define DE(str) do{ printf("===ERROR!=== %s[%d]-%s():str=%s\n", __FILE__, __LINE__, __func__, \ + str); }while(0) +// integer +#define DI(i) do{ printf("=Memblaze= %s[%d]-%s():int=%d\n", __FILE__, __LINE__, __func__, \ + (int)i); } while(0) +#define DPI(prompt, i) do{ printf("=Memblaze= %s[%d]-%s():%s=%d\n", __FILE__, __LINE__, __func__, \ + prompt, i); }while(0) +#define DAI(prompt, i, arr, max) do{ printf("=Memblaze= %s[%d]-%s():%s", __FILE__, __LINE__, __func__, prompt); \ + for(i=0;i<max;i++) printf(" %d:%d", i, arr[i]); \ + printf("\n"); }while(0) +// char +#define DC(c) do{ printf("=Memblaze= %s[%d]-%s():char=%c\n", __FILE__, __LINE__, __func__, c); \ + }while(0) +#define DPC(prompt, c) do{ printf("=Memblaze= %s[%d]-%s():%s=%c\n", __FILE__, __LINE__, __func__, \ + prompt, c); }while(0) +// address +#define DA(add) do{ printf("=Memblaze= %s[%d]-%s():address=0x%08X\n", __FILE__, __LINE__, \ + __func__, add); }while(0) +#define DPA(prompt, add) do{ printf("=Memblaze= %s[%d]-%s():%s=0x%08X\n", __FILE__, __LINE__, __func__, \ + prompt, add); }while(0) +// string +#define DS(str) do{ printf("=Memblaze= %s[%d]-%s():str=%s\n", __FILE__, __LINE__, __func__, str); \ + }while(0) +#define DPS(prompt, str) do{ printf("=Memblaze= %s[%d]-%s():%s=%s\n", __FILE__, __LINE__, __func__, \ + prompt, str); }while(0) +#define DAS(prompt, i, arr, max) do{ printf("=Memblaze= %s[%d]-%s():%s", __FILE__, __LINE__, __func__, prompt); \ + for(i=0;i<max;i++) printf(" %d:%s", i, arr[i]); \ + printf("\n"); }while(0) +// array +#define DR(str, k) do{ int ip; for(ip=0;ip<k;ip++) if(NULL != argv[ip]) \ + printf("=Memblaze= %s[%d]-%s():%d=%s\n", \ + __FILE__, __LINE__, __func__, ip, str[ip]); }while(0) +#define DARG do{ DPI("argc", argc); \ + int ip; for(ip=0;ip<argc;ip++) if(NULL != argv[ip]) \ + printf("=Memblaze= %s[%d]-%s():%d=%s\n", \ + __FILE__, __LINE__, __func__, ip, argv[ip]); }while(0) + +#define fPRINT_PARAM1(format) \ + { \ + do \ + { \ + fprintf(fdi, format);\ + if (print) \ + { \ + printf(format); \ + } \ + } while (0); \ + } + +#define fPRINT_PARAM2(format, value) \ + { \ + do \ + { \ + fprintf(fdi, format, value);\ + if (print) \ + { \ + printf(format, value); \ + } \ + } while (0); \ + } + #endif // __MEMBLAZE_UTILS_H__ diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 165fcf0..f76c015 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -14,1167 +14,2332 @@ #include "nvme-ioctl.h" #include <sys/ioctl.h> #include <limits.h> - #define CREATE_CMD #include "micron-nvme.h" -#define min(x, y) ((x) > (y) ? (y) : (x)) -#define SensorCount 2 + +/* Supported Vendor specific feature ids */ +#define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS 0xC3 +#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xCE +#define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION 0xCF +#define MICRON_FEATURE_SMBUS_OPTION 0xD5 + +/* Supported Vendor specific log page sizes */ #define C5_log_size (((452 + 16 * 1024) / 4) * 4096) -#define D0_log_size 256 +#define C0_log_size 512 +#define C2_log_size 4096 +#define D0_log_size 512 +#define FB_log_size 512 #define MaxLogChunk 16 * 1024 #define CommonChunkSize 16 * 4096 +#define min(x, y) ((x) > (y) ? (y) : (x)) +#define SensorCount 2 + +/* Plugin version major_number.minor_number.patch */ +static const char *__version_major = "1"; +static const char *__version_minor = "0"; +static const char *__version_patch = "5"; + +/* supported models of micron plugin; new models should be added at the end + * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! + */ +typedef enum { M5410 = 0, M51AX, M51BX, M51CX, M5407, M5411, UNKNOWN_MODEL } eDriveModel; + +#define MICRON_VENDOR_ID 0x1344 + +static char *fvendorid1 = "/sys/class/nvme/nvme%d/device/vendor"; +static char *fvendorid2 = "/sys/class/misc/nvme%d/device/vendor"; +static char *fdeviceid1 = "/sys/class/nvme/nvme%d/device/device"; +static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; +static unsigned short vendor_id; +static unsigned short device_id; + typedef struct _LogPageHeader_t { - unsigned char numDwordsInLogPageHeaderLo; - unsigned char logPageHeaderFormatVersion; - unsigned char logPageId; - unsigned char numDwordsInLogPageHeaderHi; - unsigned int numValidDwordsInPayload; - unsigned int numDwordsInEntireLogPage; + unsigned char numDwordsInLogPageHeaderLo; + unsigned char logPageHeaderFormatVersion; + unsigned char logPageId; + unsigned char numDwordsInLogPageHeaderHi; + unsigned int numValidDwordsInPayload; + unsigned int numDwordsInEntireLogPage; } LogPageHeader_t; -/* - * Useful Helper functions - */ +static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg) +{ + char tempFolder[PATH_MAX] = { 0 }; + FILE *fpOutFile = NULL; + sprintf(tempFolder, "%s/%s", dir, file); + if ((fpOutFile = fopen(tempFolder, "ab+")) != NULL) { + if (fwrite(data, 1, len, fpOutFile) != len) { + printf("Failed to write %s data to %s\n", msg, tempFolder); + } + fclose(fpOutFile); + } else { + printf("Failed to open %s file to write %s\n", tempFolder, msg); + } +} -static int micron_fw_commit(int fd, int select) +static int ReadSysFile(const char *file, unsigned short *id) { - struct nvme_admin_cmd cmd = { - .opcode = nvme_admin_activate_fw, - .cdw10 = 8, - .cdw12 = select, - }; - return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); + int ret = 0; + char idstr[32] = { '\0' }; + int fd = open(file, O_RDONLY); + + if (fd > 0) { + ret = read(fd, idstr, sizeof(idstr)); + close(fd); + } + + if (fd < 0 || ret < 0) + perror(file); + else + *id = strtol(idstr, NULL, 16); + + return ret; +} +static eDriveModel GetDriveModel(int idx) +{ + eDriveModel eModel = UNKNOWN_MODEL; + char path[512]; + + sprintf(path, fvendorid1, idx); + if (ReadSysFile(path, &vendor_id) < 0) { + sprintf(path, fvendorid2, idx); + ReadSysFile(path, &vendor_id); + } + sprintf(path, fdeviceid1, idx); + if (ReadSysFile(path, &device_id) < 0) { + sprintf(path, fdeviceid2, idx); + ReadSysFile(path, &device_id); + } + if (vendor_id == MICRON_VENDOR_ID) { + switch (device_id) { + case 0x51A0: + case 0x51A1: + case 0x51A2: + eModel = M51AX; + break; + case 0x51B0: + case 0x51B1: + case 0x51B2: + eModel = M51BX; + break; + case 0x51C0: + case 0x51C1: + case 0x51C2: + case 0x51C3: + eModel = M51CX; + break; + case 0x5405: + case 0x5406: + case 0x5407: + eModel = M5407; + break; + case 0x5410: + eModel = M5410; + break; + case 0x5411: + eModel = M5411; + break; + default: + break; + } + } + return eModel; } static int ZipAndRemoveDir(char *strDirName, char *strFileName) { - int err = 0; - char strBuffer[PATH_MAX]; - int nRet; - - sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, - strDirName); - - nRet = system(strBuffer); - - if (nRet < 0) { - printf("Unable to create zip package!\n"); - err = EINVAL; - goto exit_status; - } - - sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); - nRet = system(strBuffer); - if (nRet < 0) { - printf("Unable to remove temporary files!\n"); - err = EINVAL; - goto exit_status; - } - - exit_status: - err = system("rm -f temp.txt"); - return err; + int err = 0; + char strBuffer[PATH_MAX]; + int nRet; + bool is_tgz = false; + struct stat sb; + + if (strstr(strFileName, ".tar.gz") || strstr(strFileName, ".tgz")) { + sprintf(strBuffer, "tar -zcf \"%s\" \"%s\"", strFileName, + strDirName); + is_tgz = true; + } else { + sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, + strDirName); + } + + err = EINVAL; + nRet = system(strBuffer); + + /* check if log file is created, if not print error message */ + if (nRet < 0 || (stat(strFileName, &sb) == -1)) { + if (is_tgz) + sprintf(strBuffer, "check if tar and gzip commands are installed"); + else + sprintf(strBuffer, "check if zip command is installed"); + + fprintf(stderr, "Failed to create log data package, %s!\n", strBuffer); + } + + sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); + nRet = system(strBuffer); + if (nRet < 0) + printf("Failed to remove temporary files!\n"); + + err = system("rm -f temp.txt"); + return err; } static int SetupDebugDataDirectories(char *strSN, char *strFilePath, - char *strMainDirName, char *strOSDirName, - char *strCtrlDirName) + char *strMainDirName, char *strOSDirName, + char *strCtrlDirName) { - int err = 0; - char strAppend[250]; - struct stat st; - char *fileLocation = NULL; - char *fileName; - int length = 0; - int nIndex = 0; - char *strTemp = NULL; - struct stat dirStat; - int j; - int k = 0; - int i = 0; - - if (strchr(strFilePath, '/') != NULL) { - fileName = strrchr(strFilePath, '\\'); - if (fileName == NULL) { - fileName = strrchr(strFilePath, '/'); - } - - if (fileName != NULL) { - if (!strcmp(fileName, "/")) { - goto exit_status; - } - - while (strFilePath[nIndex] != '\0') { - if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) { - goto exit_status; - } - nIndex++; - } - - length = (int)strlen(strFilePath) - (int)strlen(fileName); - - if (fileName == strFilePath) { - length = 1; - } - - fileLocation = (char *)malloc(length + 1); - strncpy(fileLocation, strFilePath, length); - fileLocation[length] = '\0'; - - while (fileLocation[k] != '\0') { - if (fileLocation[k] == '\\') { - fileLocation[k] = '/'; - } - } - - length = (int)strlen(fileLocation); - - if (':' == fileLocation[length - 1]) { - strTemp = (char *)malloc(length + 2); - strcpy(strTemp, fileLocation); - strcat(strTemp, "/"); - free(fileLocation); - - length = (int)strlen(strTemp); - fileLocation = (char *)malloc(length + 1); - memcpy(fileLocation, strTemp, length + 1); - free(strTemp); - } - - if (stat(fileLocation, &st) != 0) { - free(fileLocation); - goto exit_status; - } - free(fileLocation); - } else { - goto exit_status; - } - } - - nIndex = 0; - for (i = 0; i < (int)strlen(strSN); i++) { - if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') { - strMainDirName[nIndex++] = strSN[i]; - } - } - strMainDirName[nIndex] = '\0'; - - j = 1; - while (stat(strMainDirName, &dirStat) == 0) { - strMainDirName[nIndex] = '\0'; - sprintf(strAppend, "-%d", j); - strcat(strMainDirName, strAppend); - j++; - } - - mkdir(strMainDirName, 0777); - - if (strOSDirName != NULL) { - sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); - mkdir(strOSDirName, 0777); - - } - if (strCtrlDirName != NULL) { - sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); - mkdir(strCtrlDirName, 0777); - - } - - exit_status: - return err; + int err = 0; + char strAppend[250]; + struct stat st; + char *fileLocation = NULL; + char *fileName; + int length = 0; + int nIndex = 0; + char *strTemp = NULL; + struct stat dirStat; + int j; + int k = 0; + int i = 0; + + if (strchr(strFilePath, '/') != NULL) { + fileName = strrchr(strFilePath, '\\'); + if (fileName == NULL) { + fileName = strrchr(strFilePath, '/'); + } + + if (fileName != NULL) { + if (!strcmp(fileName, "/")) { + goto exit_status; + } + + while (strFilePath[nIndex] != '\0') { + if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) { + goto exit_status; + } + nIndex++; + } + + length = (int)strlen(strFilePath) - (int)strlen(fileName); + + if (fileName == strFilePath) { + length = 1; + } + + if ((fileLocation = (char *)malloc(length + 1)) == NULL) { + goto exit_status; + } + strncpy(fileLocation, strFilePath, length); + fileLocation[length] = '\0'; + + while (fileLocation[k] != '\0') { + if (fileLocation[k] == '\\') { + fileLocation[k] = '/'; + } + k++; + } + + length = (int)strlen(fileLocation); + + if (':' == fileLocation[length - 1]) { + if ((strTemp = (char *)malloc(length + 2)) == NULL) { + goto exit_status; + } + strcpy(strTemp, fileLocation); + strcat(strTemp, "/"); + free(fileLocation); + + length = (int)strlen(strTemp); + if ((fileLocation = (char *)malloc(length + 1)) == NULL) { + goto exit_status; + } + + memcpy(fileLocation, strTemp, length + 1); + free(strTemp); + } + + if (stat(fileLocation, &st) != 0) { + free(fileLocation); + goto exit_status; + } + free(fileLocation); + } else { + goto exit_status; + } + } + + nIndex = 0; + for (i = 0; i < (int)strlen(strSN); i++) { + if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') { + strMainDirName[nIndex++] = strSN[i]; + } + } + strMainDirName[nIndex] = '\0'; + + j = 1; + while (stat(strMainDirName, &dirStat) == 0) { + strMainDirName[nIndex] = '\0'; + sprintf(strAppend, "-%d", j); + strcat(strMainDirName, strAppend); + j++; + } + + mkdir(strMainDirName, 0777); + + if (strOSDirName != NULL) { + sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); + mkdir(strOSDirName, 0777); + + } + if (strCtrlDirName != NULL) { + sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); + mkdir(strCtrlDirName, 0777); + + } + +exit_status: + return err; } static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) { - int err = 0; - struct nvme_admin_cmd cmd; - unsigned int uiXferDwords = 0; - unsigned char pTmpBuf[CommonChunkSize] = { 0 }; - LogPageHeader_t *pLogHeader = NULL; - - if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { - cmd.opcode = 0x02; - cmd.cdw10 = ucLogID; - uiXferDwords = (unsigned int)(CommonChunkSize / 4); - cmd.nsid = 0xFFFFFFFF; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - cmd.data_len = CommonChunkSize; - cmd.addr = (__u64) (uintptr_t) & pTmpBuf; - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - if (err == 0) { - pLogHeader = (LogPageHeader_t *) pTmpBuf; - LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; - *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; - } else { - printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err); - *nLogSize = 0; - } - } - return err; + int err = 0; + unsigned char pTmpBuf[CommonChunkSize] = { 0 }; + LogPageHeader_t *pLogHeader = NULL; + + if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { + err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, NVME_NO_LOG_LSP, + CommonChunkSize, pTmpBuf); + if (err == 0) { + pLogHeader = (LogPageHeader_t *) pTmpBuf; + LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; + *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; + if (pLogHeader1->logPageHeaderFormatVersion == 0) { + printf ("Unsupported log page format version %d of log page : 0x%X\n", + ucLogID, err); + *nLogSize = 0; + err = -1; + } + } else { + printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err); + *nLogSize = 0; + } + } + return err; } static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize) { - int err = 0; - struct nvme_admin_cmd cmd = { 0 }; - unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); - unsigned int uiMaxChunk = uiNumDwords; - unsigned int uiNumChunks = 1; - unsigned int uiXferDwords = 0; - unsigned long long ullBytesRead = 0; - unsigned char *pTempPtr = pBuffer; - unsigned char ucOpCode = 0x02; - - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - uiMaxChunk = 4096; - } else if (uiMaxChunk > 16 * 1024) { - uiMaxChunk = 16 * 1024; - } - - uiNumChunks = uiNumDwords / uiMaxChunk; - if (uiNumDwords % uiMaxChunk > 0) { - uiNumChunks += 1; - } - - for (unsigned int i = 0; i < uiNumChunks; i++) { - memset(&cmd, 0, sizeof(cmd)); - uiXferDwords = uiMaxChunk; - if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) { - uiXferDwords = uiNumDwords % uiMaxChunk; - } - - cmd.opcode = ucOpCode; - cmd.cdw10 |= ucLogID; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - - if (ucLogID == 0x7) { - cmd.cdw10 |= 0x80; - } - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - cmd.cdw11 = 1; - } - if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { - unsigned long long ullOffset = ullBytesRead; - cmd.cdw12 = ullOffset & 0xFFFFFFFF; - cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; - } - - cmd.addr = (__u64) (uintptr_t) pTempPtr; - cmd.nsid = 0xFFFFFFFF; - cmd.data_len = uiXferDwords * 4; - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - ullBytesRead += uiXferDwords * 4; - pTempPtr = pBuffer + ullBytesRead; - } - - return err; + int err = 0; + struct nvme_admin_cmd cmd = { 0 }; + unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); + unsigned int uiMaxChunk = uiNumDwords; + unsigned int uiNumChunks = 1; + unsigned int uiXferDwords = 0; + unsigned long long ullBytesRead = 0; + unsigned char *pTempPtr = pBuffer; + unsigned char ucOpCode = 0x02; + + if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { + uiMaxChunk = 4096; + } else if (uiMaxChunk > 16 * 1024) { + uiMaxChunk = 16 * 1024; + } + + uiNumChunks = uiNumDwords / uiMaxChunk; + if (uiNumDwords % uiMaxChunk > 0) { + uiNumChunks += 1; + } + + for (unsigned int i = 0; i < uiNumChunks; i++) { + memset(&cmd, 0, sizeof(cmd)); + uiXferDwords = uiMaxChunk; + if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) { + uiXferDwords = uiNumDwords % uiMaxChunk; + } + + cmd.opcode = ucOpCode; + cmd.cdw10 |= ucLogID; + cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; + + if (ucLogID == 0x7) { + cmd.cdw10 |= 0x80; + } + if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { + cmd.cdw11 = 1; + } + if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { + unsigned long long ullOffset = ullBytesRead; + cmd.cdw12 = ullOffset & 0xFFFFFFFF; + cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; + } + + cmd.addr = (__u64) (uintptr_t) pTempPtr; + cmd.nsid = 0xFFFFFFFF; + cmd.data_len = uiXferDwords * 4; + err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); + ullBytesRead += uiXferDwords * 4; + pTempPtr = pBuffer + ullBytesRead; + } + + return err; } static int NVMEResetLog(int nFD, unsigned char ucLogID, int nBufferSize, - long long llMaxSize) + long long llMaxSize) { - unsigned int *pBuffer = NULL; - int err = 0; + unsigned int *pBuffer = NULL; + int err = 0; - if ((pBuffer = (unsigned int *)calloc(1, nBufferSize)) == NULL) - return err; + if ((pBuffer = (unsigned int *)calloc(1, nBufferSize)) == NULL) + return err; - while (err == 0 && llMaxSize > 0) { - err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); - if (err) - return err; + while (err == 0 && llMaxSize > 0) { + err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); + if (err) + return err; - if (pBuffer[0] == 0xdeadbeef) - break; + if (pBuffer[0] == 0xdeadbeef) + break; - llMaxSize = llMaxSize - nBufferSize; - } + llMaxSize = llMaxSize - nBufferSize; + } - free(pBuffer); - return err; + free(pBuffer); + return err; } -static int GetCommonLogPage(int nFD, unsigned char ucLogID, unsigned char **pBuffer, int nBuffSize) +static int GetCommonLogPage(int nFD, unsigned char ucLogID, + unsigned char **pBuffer, int nBuffSize) { - struct nvme_admin_cmd cmd; - int err = 0; - unsigned char pTmpBuf[CommonChunkSize] = { 0 }; - unsigned int uiMaxChunk = 0; - unsigned int uiXferDwords = 0; - int nBytesRead = 0; - unsigned char *pTempPtr = NULL; - - uiMaxChunk = CommonChunkSize / 4; - pTempPtr = (unsigned char *)malloc(nBuffSize); - if (!pTempPtr) { - goto exit_status; - } - memset(pTempPtr, 0, nBuffSize); - - while (nBytesRead < nBuffSize) { - int nBytesRemaining = nBuffSize - nBytesRead; - - memset(pTmpBuf, 0, CommonChunkSize); - - uiXferDwords = uiMaxChunk; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = 0x02; - cmd.cdw10 |= ucLogID; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - - if (nBytesRead > 0) { - unsigned long long ullOffset = (unsigned long long)nBytesRead; - cmd.cdw12 = ullOffset & 0xFFFFFFFF; - cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; - } - cmd.nsid = 0xFFFFFFFF; - cmd.data_len = uiXferDwords * 4; - cmd.addr = (__u64) (uintptr_t) pTmpBuf; - - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - - if (nBytesRemaining >= (int)(uiMaxChunk * 4)) { - memcpy(&pTempPtr[nBytesRead], pTmpBuf, uiMaxChunk * 4); - } else { - memcpy(&pTempPtr[nBytesRead], pTmpBuf, nBytesRemaining); - } - - nBytesRead += (int)uiXferDwords *4; - } - *pBuffer = pTempPtr; - - exit_status: - return err; + unsigned char *pTempPtr = NULL; + int err = 0; + pTempPtr = (unsigned char *)malloc(nBuffSize); + if (!pTempPtr) { + goto exit_status; + } + memset(pTempPtr, 0, nBuffSize); + err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, NVME_NO_LOG_LSP, + nBuffSize, pTempPtr); + *pBuffer = pTempPtr; + +exit_status: + return err; } /* * Plugin Commands */ +static int micron_parse_options(int argc, char **argv, const char *desc, + const struct argconfig_commandline_options *opts, eDriveModel *modelp) +{ + int idx = 0; + int fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) { + perror("open"); + return -1; + } + + if (modelp) { + sscanf(argv[optind], "/dev/nvme%d", &idx); + *modelp = GetDriveModel(idx); + } + + return fd; +} -static int micron_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_fw_commit(int fd, int select) { - const char *desc = - "This performs a selective firmware download, which allows the user to " - "select which firmware binary to update for 9200 devices. This requires a power cycle once the " - "update completes. The options available are: \n\n" - "OOB - This updates the OOB and main firmware\n" - "EEP - This updates the eeprom and main firmware\n" - "ALL - This updates the eeprom, OOB, and main firmware"; - const char *fw = "firmware file (required)"; - const char *select = "FW Select (e.g., --select=ALL)"; - int xfer = 4096; - void *fw_buf; - int fd, selectNo, fw_fd, fw_size, err, offset = 0; - struct stat sb; - - struct config { - char *fw; - char *select; - }; - - struct config cfg = { - .fw = "", - .select = "\0", - }; - - OPT_ARGS(opts) = { - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_STRING("select", 's', "flag", &cfg.select, select), - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - - if (fd < 0) - return fd; - - if (strlen(cfg.select) != 3) { - fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; - } - - for (int i = 0; i < 3; i++) { - cfg.select[i] = toupper(cfg.select[i]); - } - - if (strncmp(cfg.select, "OOB", 3) == 0) { - selectNo = 18; - } else if (strncmp(cfg.select, "EEP", 3) == 0) { - selectNo = 10; - } else if (strncmp(cfg.select, "ALL", 3) == 0) { - selectNo = 26; - } else { - fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; - } - - fw_fd = open(cfg.fw, O_RDONLY); - if (fw_fd < 0) { - fprintf(stderr, "no firmware file provided\n"); - err = EINVAL; - goto out; - } - - err = fstat(fw_fd, &sb); - if (err < 0) { - perror("fstat"); - err = errno; - } - - fw_size = sb.st_size; - if (fw_size & 0x3) { - fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); - err = EINVAL; - goto out; - } - - if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { - fprintf(stderr, "No memory for f/w size:%d\n", fw_size); - err = ENOMEM; - goto out; - } - - if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) - return EIO; - - while (fw_size > 0) { - xfer = min(xfer, fw_size); - - err = nvme_fw_download(fd, offset, xfer, fw_buf); - if (err < 0) { - perror("fw-download"); - goto out; - } else if (err != 0) { - fprintf(stderr, "NVME Admin command error:%s(%x)\n", - nvme_status_to_string(err), err); - goto out; - } - fw_buf += xfer; - fw_size -= xfer; - offset += xfer; - } - - err = micron_fw_commit(fd, selectNo); - - if (err == 0x10B || err == 0x20B) { - err = 0; - fprintf(stderr, - "Update successful! Please power cycle for changes to take effect\n"); - } - - out: - return err; + struct nvme_admin_cmd cmd = { + .opcode = nvme_admin_activate_fw, + .cdw10 = 8, + .cdw12 = select, + }; + return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); } -static int micron_temp_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_selective_download(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { + const char *desc = + "This performs a selective firmware download, which allows the user to " + "select which firmware binary to update for 9200 devices. This requires " + "a power cycle once the update completes. The options available are: \n\n" + "OOB - This updates the OOB and main firmware\n" + "EEP - This updates the eeprom and main firmware\n" + "ALL - This updates the eeprom, OOB, and main firmware"; + const char *fw = "firmware file (required)"; + const char *select = "FW Select (e.g., --select=ALL)"; + int xfer = 4096; + void *fw_buf; + int fd, selectNo, fw_fd, fw_size, err, offset = 0; + struct stat sb; + + struct config { + char *fw; + char *select; + }; + + struct config cfg = { + .fw = "", + .select = "\0", + }; + + OPT_ARGS(opts) = { + OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), + OPT_STRING("select", 's', "flag", &cfg.select, select), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + return fd; + + if (strlen(cfg.select) != 3) { + fprintf(stderr, "Invalid select flag\n"); + err = EINVAL; + goto out; + } + + for (int i = 0; i < 3; i++) { + cfg.select[i] = toupper(cfg.select[i]); + } + + if (strncmp(cfg.select, "OOB", 3) == 0) { + selectNo = 18; + } else if (strncmp(cfg.select, "EEP", 3) == 0) { + selectNo = 10; + } else if (strncmp(cfg.select, "ALL", 3) == 0) { + selectNo = 26; + } else { + fprintf(stderr, "Invalid select flag\n"); + err = EINVAL; + goto out; + } + + fw_fd = open(cfg.fw, O_RDONLY); + if (fw_fd < 0) { + fprintf(stderr, "no firmware file provided\n"); + err = EINVAL; + goto out; + } + + err = fstat(fw_fd, &sb); + if (err < 0) { + perror("fstat"); + err = errno; + } + + fw_size = sb.st_size; + if (fw_size & 0x3) { + fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); + err = EINVAL; + goto out; + } + + if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { + fprintf(stderr, "No memory for f/w size:%d\n", fw_size); + err = ENOMEM; + goto out; + } + + if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) + return EIO; + + while (fw_size > 0) { + xfer = min(xfer, fw_size); + + err = nvme_fw_download(fd, offset, xfer, fw_buf); + if (err < 0) { + perror("fw-download"); + goto out; + } else if (err != 0) { + fprintf(stderr, "NVME Admin command error:%s(%x)\n", + nvme_status_to_string(err), err); + goto out; + } + fw_buf += xfer; + fw_size -= xfer; + offset += xfer; + } + + err = micron_fw_commit(fd, selectNo); + + if (err == 0x10B || err == 0x20B) { + err = 0; + fprintf(stderr, + "Update successful! Power cycle for changes to take effect\n"); + } + +out: + return err; +} - struct nvme_smart_log smart_log; - unsigned int temperature = 0, i = 0, fd = 0, err = 0; - unsigned int tempSensors[SensorCount] = { 0 }; - const char *desc = "Retrieve Micron temperature info for the given device "; - - OPT_ARGS(opts) = { - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; - return -1; - } - - err = nvme_smart_log(fd, 0xffffffff, &smart_log); - if (!err) { - printf("Micron temperature information:\n"); - temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); - temperature = temperature ? temperature - 273 : 0; - for (i = 0; i < SensorCount; i++) { - tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); - tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; - } - printf("%-10s : %u C\n", "Current Composite Temperature", temperature); - for (i = 0; i < SensorCount; i++) { - printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); - } - } - return err; +static int micron_smbus_option(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + __u32 result = 0; + __u32 cdw10 = 0; + __u32 cdw11 = 0; + const char *desc = "Enable/Disable/Get status of SMBUS option on controller"; + const char *option = "enable or disable or status"; + const char *value = "1 - hottest component temperature, 0 - composite " + "temperature (default) for enable option, 0 (current), " + "1 (default), 2 (saved) for status options"; + const char *save = "1 - persistent, 0 - non-persistent (default)"; + int err = 0; + int fd = 0; + int fid = MICRON_FEATURE_SMBUS_OPTION; + eDriveModel model = UNKNOWN_MODEL; + + struct { + char *option; + int value; + int save; + int status; + } opt = { + .option = "disable", + .value = 0, + .save = 0, + .status = 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("value", 'v', &opt.value, value), + OPT_UINT("save", 's', &opt.save, save), + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + if (model != M5407 && model != M5411) { + printf ("This option is not supported for specified drive\n"); + close(fd); + return err; + } + + if (!strcmp(opt.option, "enable")) { + cdw11 = opt.value << 1 | 1; + err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + if (err == 0) { + printf("successfully enabled SMBus on drive\n"); + } else { + printf("Failed to enabled SMBus on drive\n"); + } + } + else if (!strcmp(opt.option, "status")) { + cdw10 = opt.value; + err = nvme_get_feature(fd, 1, fid, cdw10, 0, 0, 0, &result); + if (err == 0) { + printf("SMBus status on the drive: %s (returns %s temperature) \n", + (result & 1) ? "enabled" : "disabled", + (result & 2) ? "hottest component" : "composite"); + } else { + printf("Failed to retrieve SMBus status on the drive\n"); + } + } + else if (!strcmp(opt.option, "disable")) { + cdw11 = opt.value << 1 | 0; + err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + if (err == 0) { + printf("Successfully disabled SMBus on drive\n"); + } else { + printf("Failed to disable SMBus on drive\n"); + } + } else { + printf("Invalid option %s, valid values are enable, disable or status\n", + opt.option); + close(fd); + return -1; + } + + close(fd); + return err; } -static int micron_pcie_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_temp_stats(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { - int err = 0, bus = 0, domain = 0, device = 0, function = 0; - char strTempFile[1024], strTempFile2[1024], command[1024]; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[NAME_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char uncorrectable[8] = { 0 }; - char *res; - - if (argc != 2) { - printf("vs-pcie-stats: Invalid argument\n"); - printf("Usage: nvme micron vs-pcie-stats <device>\n\n"); - goto out; - } - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - sprintf(strTempFile, "/sys/block/%s/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - sprintf(strTempFile, "/sys/block/%s/device/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - res = fgets(uncorrectable, sizeof(uncorrectable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - printf("PCIE Stats:\n"); - printf("Device correctable errors detected: %s\n", correctable); - printf("Device uncorrectable errors detected: %s\n", uncorrectable); - - out: - return err; + + struct nvme_smart_log smart_log; + unsigned int temperature = 0, i = 0, err = 0; + unsigned int tempSensors[SensorCount] = { 0 }; + const char *desc = "Retrieve Micron temperature info for the given device "; + const char *fmt = "output format normal|json"; + struct format { + char *fmt; + }; + struct format cfg = { + .fmt = "normal", + }; + bool is_json = false; + struct json_object *root; + struct json_object *logPages; + int fd; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + err = nvme_smart_log(fd, 0xffffffff, &smart_log); + if (!err) { + temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); + temperature = temperature ? temperature - 273 : 0; + for (i = 0; i < SensorCount; i++) { + tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); + tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; + } + if (is_json) { + struct json_object *stats = json_create_object(); + char tempstr[64] = { 0 }; + root = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Micron temperature information", logPages); + sprintf(tempstr, "%u C", temperature); + json_object_add_value_string(stats, "Current Composite Temperature", tempstr); + for (i = 0; i < SensorCount; i++) { + char sensor_str[256] = { 0 }; + char datastr[64] = { 0 }; + sprintf(sensor_str, "Temperature Sensor #%d", (i + 1)); + sprintf(datastr, "%u C", tempSensors[i]); + json_object_add_value_string(stats, sensor_str, datastr); + } + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + printf("Micron temperature information:\n"); + printf("%-10s : %u C\n", "Current Composite Temperature", temperature); + for (i = 0; i < SensorCount; i++) { + printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); + } + } + } + return err; } -static int micron_clear_pcie_correctable_errors(int argc, char **argv, - struct command *cmd, - struct plugin *plugin) +static int micron_pcie_stats(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { - int err = 0, bus = 0, domain = 0, device = 0, function = 0; - char strTempFile[1024], strTempFile2[1024], command[1024]; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[PATH_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char *res; - - if (argc != 2) { - printf("clear-pcie-correctable-errors: Invalid argument\n"); - printf ("Usage: nvme micron clear-pcie-correctable-errors <device>\n\n"); - goto out; - } - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device", devicename); - if (err < 0) - goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device/device", devicename); - if (err < 0) - goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, - device, function); - - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to clear error count\n"); - goto out; - } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - printf("Device correctable errors cleared!\n"); - printf("Device correctable errors detected: %s\n", correctable); - - out: - return err; + int i, fd, err = 0, bus = 0, domain = 0, device = 0, function = 0, ctrlIdx; + char strTempFile[1024], strTempFile2[1024], command[1024]; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[NAME_MAX] = { 0 }; + ssize_t sLinkSize = 0; + FILE *fp; + char correctable[8] = { 0 }; + char uncorrectable[8] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + char *res; + bool is_json = true; + struct format { + char *fmt; + }; + const char *desc = "Retrieve PCIe event counters"; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + struct { + char *err; + int bit; + int val; + } pcie_correctable_errors[] = { + { "Unsupported Request Error Status (URES)", 20}, + { "ECRC Error Status (ECRCES)", 19}, + { "Malformed TLP Status (MTS)", 18}, + { "Receiver Overflow Status (ROS)", 17}, + { "Unexpected Completion Status (UCS)", 16}, + { "Completer Abort Status (CAS)", 15}, + { "Completion Timeout Stats (CTS)", 14}, + { "Flow Control Protocol Error Status (FCPES)", 13}, + { "Poisoned TLP Status (PTS)", 12}, + { "Data Link Protocol Error Status (DLPES)", 4}, + }, + pcie_uncorrectable_errors[] = { + { "Advisory Non-Fatal Error Status (ANFES)", 13}, + { "Replay Timer Timeout Status (RTS)", 12}, + { "REPLY NUM Rollover Status (RRS)", 8}, + { "Bad DLLP Status (BDS)", 7}, + { "Bad TLP Status (BTS)", 6}, + { "Receiver Error Status (RES)", 0}, + }; + + __u32 correctable_errors; + __u32 uncorrectable_errors; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-pcie-stats command\n"); + close(fd); + goto out; + } + + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + sprintf(strTempFile, "/sys/block/%s/device", devicename); + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + sprintf(strTempFile, "/sys/block/%s/device/device", devicename); + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(uncorrectable, sizeof(uncorrectable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + + correctable_errors = (__u32)strtol(correctable, NULL, 16); + uncorrectable_errors = (__u32)strtol(uncorrectable, NULL, 16); + + if (is_json) { + + struct json_object *root = json_create_object(); + struct json_object *pcieErrors = json_create_array(); + struct json_object *stats = json_create_object(); + + json_object_add_value_array(root, "PCIE Stats", pcieErrors); + for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { + json_object_add_value_int(stats, pcie_correctable_errors[i].err, + ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); + } + for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { + json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, + ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); + } + json_array_add_value_object(pcieErrors, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else if (eModel == M5407 || eModel == M5410) { + for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { + printf("%-40s : %-1d\n", pcie_correctable_errors[i].err, + ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); + } + for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { + printf("%-40s : %-1d\n", pcie_uncorrectable_errors[i].err, + ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); + } + } else { + printf("PCIE Stats:\n"); + printf("Device correctable errors detected: %s\n", correctable); + printf("Device uncorrectable errors detected: %s\n", uncorrectable); + } + +out: + return err; } -static int micron_nand_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_clear_pcie_correctable_errors(int argc, char **argv, + struct command *cmd, + struct plugin *plugin) { - const char *desc = "Retrieve Micron NAND stats for the given device "; - unsigned int extSmartLog[64] = { 0 }; - struct nvme_id_ctrl ctrl; - int fd, err; - - OPT_ARGS(opts) = { - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; - return -1; - } - - err = nvme_identify_ctrl(fd, &ctrl); - if (err) - goto out; - - err = NVMEGetLogPage(fd, 0xD0, (unsigned char *)extSmartLog, D0_log_size); - if (err) - goto out; - - unsigned long long count = ((unsigned long long)extSmartLog[45] << 32) | extSmartLog[44]; - printf("%-40s : 0x%llx\n", "NAND Writes (Bytes Written)", count); - printf("%-40s : ", "Program Failure Count"); - - unsigned long long count_hi = ((unsigned long long)extSmartLog[39] << 32) | extSmartLog[38]; - unsigned long long count_lo = ((unsigned long long)extSmartLog[37] << 32) | extSmartLog[36]; - if (count_hi != 0) - printf("0x%llx%016llx", count_hi, count_lo); - else - printf("0x%llx\n", count_lo); - - count = ((unsigned long long)extSmartLog[25] << 32) | extSmartLog[24]; - printf("%-40s : 0x%llx\n", "Erase Failures", count); - printf("%-40s : 0x%x\n", "Bad Block Count", extSmartLog[3]); - - count = (unsigned long long)extSmartLog[3] - (count_lo + count); - printf("%-40s : 0x%llx\n", "NAND XOR/RAID Recovery Trigger Events", count); - printf("%-40s : 0x%x\n", "NSZE Change Supported", (ctrl.oacs >> 3) & 0x1); - printf("%-40s : 0x%x\n", "Number of NSZE Modifications", extSmartLog[1]); - out: - close(fd); - return err; + int err = -EINVAL, bus = 0, domain = 0, device = 0, function = 0; + char strTempFile[1024], strTempFile2[1024], command[1024]; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[PATH_MAX] = { 0 }; + ssize_t sLinkSize = 0; + eDriveModel model = UNKNOWN_MODEL; + char correctable[8] = { 0 }; + int fd = -1; + FILE *fp; + char *res; + const char *desc = "Clear PCIe Device Correctable Errors"; + __u32 result = 0; + __u8 fid = MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + /* For M51CX models, PCIe errors are cleared using 0xC3 feature */ + if (model == M51CX) { + err = nvme_set_feature(fd, 0, fid, (1 << 31), 0, 0, 0, 0, &result); + if (err == 0 && (err = (int)result) == 0) + printf("Device correctable errors cleared!\n"); + else + printf("Error clearing Device correctable errors = 0x%x\n", err); + goto out; + } + + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device", devicename); + if (err < 0) + goto out; + + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device/device", devicename); + if (err < 0) + goto out; + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, + device, function); + err = -1; + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to clear error count\n"); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + printf("Device correctable errors cleared!\n"); + printf("Device correctable errors detected: %s\n", correctable); + err = 0; +out: + if (fd > 0) + close(fd); + return err; } -typedef enum { M5410 = 0, M51AX, M51BX, UNKNOWN_MODEL } eDriveModel; +static struct logpage { + const char *field; + char datastr[128]; +} d0_log_page[] = { + { "NAND Writes (Bytes Written)", { 0 }}, + { "Program Failure Count", { 0 }}, + { "Erase Failures", { 0 }}, + { "Bad Block Count", { 0 }}, + { "NAND XOR/RAID Recovery Trigger Events", { 0 }}, + { "NSZE Change Supported", { 0 }}, + { "Number of NSZE Modifications", { 0 }} +}; + +static void init_d0_log_page(__u8 *buf, __u8 nsze) +{ + unsigned int logD0[D0_log_size/sizeof(int)] = { 0 }; + __u64 count_lo, count_hi, count; -static char *fvendorid1 = "/sys/class/nvme/nvme%d/device/vendor"; -static char *fvendorid2 = "/sys/class/misc/nvme%d/device/vendor"; -static char *fdeviceid1 = "/sys/class/nvme/nvme%d/device/device"; -static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; -static unsigned short vendor_id; -static unsigned short device_id; + memcpy(logD0, buf, sizeof(logD0)); -static int ReadSysFile(const char *file, unsigned short *id) + + count = ((__u64)logD0[45] << 32) | logD0[44]; + sprintf(d0_log_page[0].datastr, "0x%"PRIx64, le64_to_cpu(count)); + + count_hi = ((__u64)logD0[39] << 32) | logD0[38]; + count_lo = ((__u64)logD0[37] << 32) | logD0[36]; + if (count_hi != 0) + sprintf(d0_log_page[1].datastr, "0x%"PRIx64"%016"PRIx64, + le64_to_cpu(count_hi), le64_to_cpu(count_lo)); + else + sprintf(d0_log_page[1].datastr, "0x%"PRIx64, le64_to_cpu(count_lo)); + + count = ((__u64)logD0[25] << 32) | logD0[24]; + sprintf(d0_log_page[2].datastr, "0x%"PRIx64, le64_to_cpu(count)); + + sprintf(d0_log_page[3].datastr, "0x%x", logD0[3]); + + count_lo = ((__u64)logD0[37] << 32) | logD0[36]; + count = ((__u64)logD0[25] << 32) | logD0[24]; + count = (__u64)logD0[3] - (count_lo + count); + sprintf(d0_log_page[4].datastr, "0x%"PRIx64, le64_to_cpu(count)); + + sprintf(d0_log_page[5].datastr, "0x%x", nsze); + sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]); +} + +/* OCP and Vendor specific log data format */ +struct micron_vs_logpage { + char *field; + int size; +} +/* Smart Health Log information as per OCP spec */ +ocp_c0_log_page[] = { + { "Physical Media Units Written", 16 }, + { "Physical Media Units Read", 16 }, + { "Raw Bad User NAND Block Count", 6}, + { "Normalized Bad User NAND Block Count", 2}, + { "Raw Bad System NAND Block Count", 6}, + { "Normalized Bad System NAND Block Count", 2}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "Soft ECC Error Count", 8}, + { "SSD End to End Detected Counts", 4}, + { "SSD End to End Corrected Errors", 4}, + { "System data % life-used", 1}, + { "Refresh Count", 7}, + { "Maximum User Data Erase Count", 4}, + { "Minimum User Data Erase Count", 4}, + { "Thermal Throttling Count", 1}, + { "Thermal Throttling Status", 1}, + { "Reserved", 6}, + { "PCIe Correctable Error count", 8}, + { "Incomplete Shutdowns", 4}, + { "Reserved", 4}, + { "% Free Blocks", 1}, + { "Reserved", 7}, + { "Capacitor Health", 2}, + { "Reserved", 6}, + { "Unaligned I/O", 8}, + { "Security Version Number", 8}, + { "NUSE", 8}, + { "PLP Start Count", 16}, + { "Endurance Estimate", 16}, + { "Reserved", 302}, + { "Log Page Version", 2}, + { "Log Page GUID", 16}, +}, +/* Vendor Specific Health Log information */ +fb_log_page[] = { + { "Physical Media Units Written - TLC", 16 }, + { "Physical Media Units Written - SLC", 16 }, + { "Normalized Bad User NAND Block Count", 2}, + { "Raw Bad User NAND Block Count", 6}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "SSD End to End Corrected Errors", 8}, + { "SSD End to End Detected Counts", 4}, + { "SSD End to End Uncorrected Counts", 4}, + { "System data % life-used", 1}, + { "Minimum User Data Erase Count - TLC", 8}, + { "Maximum User Data Erase Count - TLC", 8}, + { "Minimum User Data Erase Count - SLC", 8}, + { "Maximum User Data Erase Count - SLC", 8}, + { "Normalized Program Fail Count", 2}, + { "Raw Program Fail Count", 6}, + { "Normalized Erase Fail Count", 2}, + { "Raw Erase Fail Count", 6}, + { "Pcie Correctable Error Count", 8}, + { "% Free Blocks (User)", 1}, + { "Security Version Number", 8}, + { "% Free Blocks (System)", 1}, + { "Dataset Management (Deallocate) Commands", 16}, + { "Incomplete TRIM Data", 8}, + { "% Age of Completed TRIM", 1}, + { "Background Back-Pressure Gauge", 1}, + { "Soft ECC Error Count", 8}, + { "Refresh Count", 8}, + { "Normalized Bad System NAND Block Count", 2}, + { "Raw Bad System NAND Block Count", 6}, + { "Endurance Estimate", 16}, + { "Thermal Throttling Count", 1}, + { "Thermal Throttling Status", 1}, + { "Unaligned I/O", 8}, + { "Physical Media Units Read", 16}, + { "Reserved", 279}, + { "Log Page Version", 2} +}; + +/* Common function to print Micron VS log pages */ +static void print_micron_vs_logs( + __u8 *buf, /* raw log data */ + struct micron_vs_logpage *log_page, /* format of the data */ + int field_count, /* log field count */ + struct json_object *stats /* json object to add fields */ +) { - int ret = 0; - char idstr[32] = { '\0' }; - int fd = open(file, O_RDONLY); + __u64 lval_lo, lval_hi; + __u32 ival; + __u16 sval; + __u8 cval, lval[8] = { 0 }; + int field, guid_index; + int offset = 0; + + for (field = 0; field < field_count; field++) { + char datastr[1024] = { 0 }; + if (log_page[field].size == 16) { + if (strstr(log_page[field].field, "GUID")) { + char *tmpstr = datastr; + tmpstr += sprintf(datastr, "0x"); + for(guid_index = 0; guid_index < 16; guid_index++) + tmpstr += sprintf(tmpstr, "%01X", buf[offset + guid_index]); + } else { + lval_lo = *((__u64 *)(&buf[offset])); + lval_hi = *((__u64 *)(&buf[offset + 8])); + if (lval_hi) + sprintf(datastr, "0x%"PRIx64"_%"PRIx64"", + le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); + else + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } + } else if (log_page[field].size == 8) { + lval_lo = *((__u64 *)(&buf[offset])); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } else if (log_page[field].size == 7) { + /* 7 bytes will be in little-endian format, with last byte as MSB */ + memcpy(&lval[0], &buf[offset], 7); + memcpy((void *)&lval_lo, lval, 8); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } else if (log_page[field].size == 6) { + ival = *((__u32 *)(&buf[offset])); + sval = *((__u16 *)(&buf[offset + 4])); + lval_lo = (((__u64)sval << 32) | ival); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } else if (log_page[field].size == 4) { + ival = *((__u32 *)(&buf[offset])); + sprintf(datastr, "0x%x", le32_to_cpu(ival)); + } else if (log_page[field].size == 2) { + sval = *((__u16 *)(&buf[offset])); + sprintf(datastr, "0x%04x", le16_to_cpu(sval)); + } else if (log_page[field].size == 1) { + cval = buf[offset]; + sprintf(datastr, "0x%02x", cval); + } else { + sprintf(datastr, "0"); + } + offset += log_page[field].size; + /* do not print reserved values */ + if (strstr(log_page[field].field, "Reserved")) + continue; + if (stats != NULL) { + json_object_add_value_string(stats, log_page[field].field, datastr); + } else { + printf("%-40s : %-4s\n", log_page[field].field, datastr); + } + } +} - if (fd > 0) { - ret = read(fd, idstr, sizeof(idstr)); - close(fd); - } +static void print_smart_cloud_health_log(__u8 *buf, bool is_json) +{ + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = sizeof(ocp_c0_log_page)/sizeof(ocp_c0_log_page[0]); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "OCP SMART Cloud Health Log: 0xC0", + logPages); + } + + print_micron_vs_logs(buf, ocp_c0_log_page, field_count, stats); + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } +} - if (fd < 0 || ret < 0) - perror(file); - else - *id = strtol(idstr, NULL, 16); +static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json) +{ + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = sizeof(fb_log_page)/sizeof(fb_log_page[0]); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Extended Smart Log Page : 0xFB", + logPages); + } + + print_micron_vs_logs(buf, fb_log_page, field_count, stats); + + /* print last three entries from D0 log page */ + init_d0_log_page(buf2, nsze); + + if (is_json) { + for (int i = 4; i < 7; i++) { + json_object_add_value_string(stats, + d0_log_page[i].field, + d0_log_page[i].datastr); + } + } else { + for (int i = 4; i < 7; i++) { + printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); + } + } + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } +} - return ret; +static void print_nand_stats_d0(__u8 *buf, __u8 oacs, bool is_json) +{ + init_d0_log_page(buf, oacs); + + if (is_json) { + struct json_object *root = json_create_object(); + struct json_object *stats = json_create_object(); + struct json_object *logPages = json_create_array(); + + json_object_add_value_array(root, + "Extended Smart Log Page : 0xD0", + logPages); + + for (int i = 0; i < 7; i++) { + json_object_add_value_string(stats, + d0_log_page[i].field, + d0_log_page[i].datastr); + } + + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + for (int i = 0; i < 7; i++) { + printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); + } + } } -static eDriveModel GetDriveModel(int idx) +static bool nsze_from_oacs = false; /* read nsze for now from idd[4059] */ + +static int micron_nand_stats(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { - eDriveModel eModel = UNKNOWN_MODEL; - char path[512]; - - sprintf(path, fvendorid1, idx); - if (ReadSysFile(path, &vendor_id) < 0) { - sprintf(path, fvendorid2, idx); - ReadSysFile(path, &vendor_id); - } - sprintf(path, fdeviceid1, idx); - if (ReadSysFile(path, &device_id) < 0) { - sprintf(path, fdeviceid2, idx); - ReadSysFile(path, &device_id); - } - - if (vendor_id == 0x1344) { - switch (device_id) { - case 0x5410: - eModel = M5410; - break; - case 0x51A0: - case 0x51A1: - case 0x51A2: - eModel = M51AX; - break; - case 0x51B0: - case 0x51B1: - case 0x51B2: - eModel = M51BX; - break; - default: - break; - } - } - return eModel; + const char *desc = "Retrieve Micron NAND stats for the given device "; + unsigned int extSmartLog[D0_log_size/sizeof(int)] = { 0 }; + unsigned int logFB[FB_log_size/sizeof(int)] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + int fd, err, ctrlIdx; + __u8 nsze; + bool has_d0_log = true; + bool has_fb_log = false; + bool is_json = true; + struct format { + char *fmt; + }; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-nand-stats command\n"); + close(fd); + goto out; + } + + err = nvme_get_log(fd, NVME_NSID_ALL, 0xD0, false, NVME_NO_LOG_LSP, + D0_log_size, extSmartLog); + has_d0_log = (0 == err); + + /* should check for firmware version if this log is supported or not */ + if (eModel == M5407 || eModel == M5410) { + err = nvme_get_log(fd, NVME_NSID_ALL, 0xFB, false, NVME_NO_LOG_LSP, + FB_log_size, logFB); + has_fb_log = (0 == err); + } + + nsze = (ctrl.vs[987] == 0x12); + if (nsze == 0 && nsze_from_oacs) + nsze = ((ctrl.oacs >> 3) & 0x1); + err = 0; + if (has_fb_log) + print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json); + else if (has_d0_log) + print_nand_stats_d0((__u8 *)extSmartLog, nsze, is_json); + else { + printf("Unable to retrieve extended smart log for the drive\n"); + err = -ENOTTY; + } +out: + close(fd); + return err; } + static void GetDriveInfo(const char *strOSDirName, int nFD, - struct nvme_id_ctrl *ctrlp) + struct nvme_id_ctrl *ctrlp) { - FILE *fpOutFile = NULL; - char tempFile[256] = { 0 }; - char strBuffer[1024] = { 0 }; - char model[41] = { 0 }; - char serial[21] = { 0 }; - char fwrev[9] = { 0 }; - char *strPDir = strdup(strOSDirName); - char *strDest = dirname(strPDir); - - sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); - fpOutFile = fopen(tempFile, "w+"); - if (!fpOutFile) { - printf("Unable to create %s\n", tempFile); - free(strPDir); - return; - } - - strncpy(model, ctrlp->mn, 40); - strncpy(serial, ctrlp->sn, 20); - strncpy(fwrev, ctrlp->fr, 8); - - sprintf(strBuffer, - "********************\nDrive Info\n********************\n"); - - fprintf(fpOutFile, "%s", strBuffer); - sprintf(strBuffer, - "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", - "Device Name", nFD, - "Model No", (char *)model, - "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); - - fprintf(fpOutFile, "%s", strBuffer); - - sprintf(strBuffer, - "\n********************\nPCI Info\n********************\n"); - - fprintf(fpOutFile, "%s", strBuffer); - - sprintf(strBuffer, - "%-22s : %04X\n%-22s : %04X\n", - "VendorId", vendor_id, "DeviceId", device_id); - fprintf(fpOutFile, "%s", strBuffer); - fclose(fpOutFile); - free(strPDir); + FILE *fpOutFile = NULL; + char tempFile[256] = { 0 }; + char strBuffer[1024] = { 0 }; + char model[41] = { 0 }; + char serial[21] = { 0 }; + char fwrev[9] = { 0 }; + char *strPDir = strdup(strOSDirName); + char *strDest = dirname(strPDir); + + sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); + fpOutFile = fopen(tempFile, "w+"); + if (!fpOutFile) { + printf("Failed to create %s\n", tempFile); + free(strPDir); + return; + } + + strncpy(model, ctrlp->mn, 40); + strncpy(serial, ctrlp->sn, 20); + strncpy(fwrev, ctrlp->fr, 8); + + sprintf(strBuffer, + "********************\nDrive Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + sprintf(strBuffer, + "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", + "Device Name", nFD, + "Model No", (char *)model, + "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "\n********************\nPCI Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "%-22s : %04X\n%-22s : %04X\n", + "VendorId", vendor_id, "DeviceId", device_id); + fprintf(fpOutFile, "%s", strBuffer); + fclose(fpOutFile); + free(strPDir); } static void GetTimestampInfo(const char *strOSDirName) { - char outstr[200]; - time_t t; - struct tm *tmp; - FILE *fpOutFile = NULL; - size_t num; - char tempFolder[256] = { 0 }; - char *strPDir; - char *strDest; - - t = time(NULL); - tmp = localtime(&t); - if (tmp == NULL) - return; - - num = strftime(outstr, sizeof(outstr), "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); - if (num) { - strPDir = strdup(strOSDirName); - strDest = dirname(strPDir); - sprintf(tempFolder, "%s/%s", strDest, "timestamp_info.txt"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(outstr, 1, num, fpOutFile) != num) - printf("Unable to write to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - free(strPDir); - } + __u8 outstr[1024]; + time_t t; + struct tm *tmp; + size_t num; + char *strPDir; + char *strDest; + + t = time(NULL); + tmp = localtime(&t); + if (tmp == NULL) + return; + + num = strftime((char *)outstr, sizeof(outstr), + "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); + num += sprintf((char *)(outstr + num), "\nPackage Version: 1.4"); + if (num) { + strPDir = strdup(strOSDirName); + strDest = dirname(strPDir); + WriteData(outstr, num, strDest, "timestamp_info.txt", "timestamp"); + free(strPDir); + } } -static void GetCtrlIDDInfo(const char *strCtrlDirName, struct nvme_id_ctrl *ctrlp) +static void GetCtrlIDDInfo(const char *dir, struct nvme_id_ctrl *ctrlp) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile; - sprintf(tempFolder, "%s/%s", strCtrlDirName, - "nvme_controller_identify_data.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(ctrlp, 1, sizeof(*ctrlp), fpOutFile) != sizeof(*ctrlp)) - printf("Unable to write controller data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); + WriteData((__u8*)ctrlp, sizeof(*ctrlp), dir, + "nvme_controller_identify_data.bin", "id-ctrl"); } -static void GetSmartlogData(int fd, const char *strCtrlDirName) +static void GetSmartlogData(int fd, const char *dir) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - struct nvme_smart_log smart_log; - if (nvme_smart_log(fd, -1, &smart_log) == 0) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, "smart_data.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(&smart_log, 1, sizeof(smart_log), fpOutFile) != sizeof(smart_log)) - printf("Unable to write smart log data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } + struct nvme_smart_log smart_log; + if (nvme_smart_log(fd, -1, &smart_log) == 0) { + WriteData((__u8*)&smart_log, sizeof(smart_log), dir, + "smart_data.bin", "smart log"); + } } -static void GetErrorlogData(int fd, int entries, const char *strCtrlDirName) +static void GetErrorlogData(int fd, int entries, const char *dir) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - int logSize = entries * sizeof(struct nvme_error_log_page); - struct nvme_error_log_page *error_log = (struct nvme_error_log_page *)calloc(1, logSize); - - if (error_log == NULL) - return; - - if (nvme_error_log(fd, entries, error_log) == 0) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - "error_information_log.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(error_log, 1, logSize, fpOutFile) != logSize) - printf("Unable to write error log to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } - free(error_log); + int logSize = entries * sizeof(struct nvme_error_log_page); + struct nvme_error_log_page *error_log = + (struct nvme_error_log_page *)calloc(1, logSize); + + if (error_log == NULL) + return; + + if (nvme_error_log(fd, entries, error_log) == 0) { + WriteData((__u8*)error_log, logSize, dir, + "error_information_log.bin", "error log"); + } + + free(error_log); } -static void GetNSIDDInfo(int fd, const char *strCtrlDirName, int nsid) +static void GetNSIDDInfo(int fd, const char *dir, int nsid) { - char tempFolder[256] = { 0 }; - char strFileName[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - struct nvme_id_ns ns; - if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) { - sprintf(tempFolder, "identify_namespace_%d_data.bin.bin", nsid); - sprintf(strFileName, "%s/%s", strCtrlDirName, tempFolder); - fpOutFile = fopen(strFileName, "wb"); - if (fwrite(&ns, 1, sizeof(ns), fpOutFile) != sizeof(ns)) - printf("Unable to write controller data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } + char file[PATH_MAX] = { 0 }; + struct nvme_id_ns ns; + + if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) { + sprintf(file, "identify_namespace_%d_data.bin", nsid); + WriteData((__u8*)&ns, sizeof(ns), dir, file, "id-ns"); + } } static void GetOSConfig(const char *strOSDirName) { - FILE *fpOSConfig = NULL; - char strBuffer[1024], strTemp[1024]; - char strFileName[PATH_MAX]; - int i; - - struct { - char *strcmdHeader; - char *strCommand; - } cmdArray[] = { - { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, - { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, - { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, - { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, - { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, - { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, - { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, - { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, - { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } - }; - - sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); - - for (i = 0; i < 7; i++) { - fpOSConfig = fopen(strFileName, "a+"); - fprintf(fpOSConfig, - "\n\n\n\n%s\n-----------------------------------------------\n", - cmdArray[i].strcmdHeader); - if (NULL != fpOSConfig) { - fclose(fpOSConfig); - fpOSConfig = NULL; - } - strcpy(strTemp, cmdArray[i].strCommand); - sprintf(strBuffer, strTemp, strFileName); - if (system(strBuffer)) - fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); - } + FILE *fpOSConfig = NULL; + char strBuffer[1024], strTemp[1024]; + char strFileName[PATH_MAX]; + int i; + + struct { + char *strcmdHeader; + char *strCommand; + } cmdArray[] = { + { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, + { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, + { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, + { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, + { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, + { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, + { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, + { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, + { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } + }; + + sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); + + for (i = 0; i < 7; i++) { + fpOSConfig = fopen(strFileName, "a+"); + fprintf(fpOSConfig, + "\n\n\n\n%s\n-----------------------------------------------\n", + cmdArray[i].strcmdHeader); + if (NULL != fpOSConfig) { + fclose(fpOSConfig); + fpOSConfig = NULL; + } + strcpy(strTemp, cmdArray[i].strCommand); + sprintf(strBuffer, strTemp, strFileName); + if (system(strBuffer)) + fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); + } } -static int micron_internal_logs(int argc, char **argv, struct command *cmd, - struct plugin *plugin) +static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, + int *logSize, int da) +{ + int err; + unsigned short data_area[4]; + unsigned char ctrl_init = (type == 0x8); + + __u8 *buffer = (unsigned char *)calloc(512, 1); + if (buffer == NULL) + return -1; + err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, 512, 0); + if (err != 0) { + fprintf(stderr, "Failed to get telemetry log header for 0x%X\n", type); + if (buffer != NULL) { + free(buffer); + } + return err; + } + + // compute size of the log + data_area[1] = buffer[9] << 16 | buffer[8]; + data_area[2] = buffer[11] << 16 | buffer[10]; + data_area[3] = buffer[13] << 16 | buffer[12]; + data_area[0] = data_area[1] > data_area[2] ? data_area[1] : data_area[2]; + data_area[0] = data_area[3] > data_area[0] ? data_area[3] : data_area[0]; + + if (data_area[da] == 0) { + fprintf(stderr, "Requested telemetry data for 0x%X is empty\n", type); + if (buffer != NULL) { + free(buffer); + buffer = NULL; + } + return -1; + } + + *logSize = data_area[da] * 512; + if ((buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize))) != NULL) { + err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, *logSize, 0); + } + + if (err == 0 && buffer != NULL) { + *data = buffer; + } else { + fprintf(stderr, "Failed to get telemetry data for 0x%x\n", type); + if (buffer != NULL) + free(buffer); + } + + return err; +} + +static int GetTelemetryData(int fd, const char *dir) +{ + unsigned char *buffer = NULL; + int i, err, logSize = 0; + char msg[256] = {0}; + struct { + __u8 log; + char *file; + } tmap[] = { + {0x07, "nvme_host_telemetry.bin"}, + {0x08, "nvme_cntrl_telemetry.bin"}, + }; + + for(i = 0; i < (int)(sizeof(tmap)/sizeof(tmap[0])); i++) { + err = micron_telemetry_log(fd, 0, tmap[i].log, &buffer, &logSize, 0); + if (err == 0 && logSize > 0 && buffer != NULL) { + sprintf(msg, "telemetry log: 0x%X", tmap[i].log); + WriteData(buffer, logSize, dir, tmap[i].file, msg); + if (buffer != NULL) + free(buffer); + } + buffer = NULL; + logSize = 0; + } + return err; +} + +static int GetFeatureSettings(int fd, const char *dir) +{ + unsigned char *bufp, buf[4096] = { 0 }; + int i, err, len, errcnt = 0; + __u32 attrVal = 0; + char msg[256] = { 0 }; + + struct features { + int id; + char *file; + } fmap[] = { + {0x01, "nvme_feature_setting_arbitration.bin"}, + {0x02, "nvme_feature_setting_pm.bin"}, + {0x03, "nvme_feature_setting_lba_range_namespace_1.bin"}, + {0x04, "nvme_feature_setting_temp_threshold.bin"}, + {0x05, "nvme_feature_setting_error_recovery.bin"}, + {0x06, "nvme_feature_setting_volatile_write_cache.bin"}, + {0x07, "nvme_feature_setting_num_queues.bin"}, + {0x08, "nvme_feature_setting_interrupt_coalescing.bin"}, + {0x09, "nvme_feature_setting_interrupt_vec_config.bin"}, + {0x0A, "nvme_feature_setting_write_atomicity.bin"}, + {0x0B, "nvme_feature_setting_async_event_config.bin"}, + {0x80, "nvme_feature_setting_sw_progress_marker.bin"}, + }; + + for (i = 0; i < (int)(sizeof(fmap)/sizeof(fmap[0])); i++) { + if (fmap[i].id == 0x03) { + len = 4096; + bufp = (unsigned char *)(&buf[0]); + } else { + len = 0; + bufp = NULL; + } + + err = nvme_get_feature(fd, 1, fmap[i].id, 0, 0x0, len, bufp, &attrVal); + if (err == 0) { + sprintf(msg, "feature: 0x%X", fmap[i].id); + WriteData((__u8*)&attrVal, sizeof(attrVal), dir, fmap[i].file, msg); + if (bufp != NULL) { + WriteData(bufp, len, dir, fmap[i].file, msg); + } + } else { + printf("Failed to retrieve feature 0x%x data !\n", fmap[i].id); + errcnt++; + } + } + return (int)(errcnt == sizeof(fmap)/sizeof(fmap[0])); +} + +static int micron_drive_info(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Get drive HW information"; + int fd, err = 0; + struct nvme_id_ctrl ctrl = { 0 }; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, NULL)) < 0) + return err; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) { + fprintf(stderr, "ERROR : nvme_identify_ctrl() failed with 0x%x\n", err); + return -1; + } + + printf("%u.%u\n", ctrl.vs[820], ctrl.vs[821]); + return 0; +} + +static int micron_cloud_ssd_plugin_version(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + printf("nvme-cli Micron cloud SSD plugin version: %s.%s\n", + __version_major, __version_minor); + return 0; +} + +static int micron_plugin_version(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("nvme-cli Micron plugin version: %s.%s.%s\n", + __version_major, __version_minor, __version_patch); + return 0; +} + +static int micron_logpage_dir(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("This command is not implemented for the drive\n"); + return 0; +} +/* Binary format of firmware activation history entry */ +struct __attribute__((__packed__)) fw_activation_history_entry { + __u8 version; + __u8 length; + __u16 rsvd1; + __le16 valid; + __le64 power_on_hour; + __le64 rsvd2; + __le64 power_cycle_count; + __u8 previous_fw[8]; + __u8 activated_fw[8]; + __u8 slot; + __u8 commit_action_type; + __le16 result; + __u8 rsvd3[14]; +}; + + +/* Binary format for firmware activation history table */ +struct __attribute__((__packed__)) micron_fw_activation_history_table { + __u8 log_page; + __u8 rsvd1[3]; + __le32 num_entries; + struct fw_activation_history_entry entries[20]; + __u8 rsvd2[2790]; + __u16 version; + __u8 GUID[16]; +}; + +/* header to be printed field widths = 10 | 12 | 10 | 11 | 12 | 9 | 9 | 9 */ + +const char *fw_activation_history_table_header = "\ +__________________________________________________________________________________\n\ + | | | | | | | \n\ +Firmware | Power | Power | Previous | New FW | Slot | Commit | Result \n\ +Activation| On Hour | cycle | firmware | activated | number | Action | \n\ +Counter | | count | | | | Type | \n\ +__________|___________|_________|__________|___________|________|________|________\n"; + +static int display_fw_activate_entry ( + int entry_count, + struct fw_activation_history_entry *entry, + char *formatted_entry, + struct json_object *stats +) +{ + time_t timestamp, hours; + char buffer[32]; + __u8 minutes, seconds; + char *ca[] = {"000b", "001b", "010b", "011b"}; + char *ptr = formatted_entry; + int index = 0, entry_size = 82; + + if (entry->version != 1 || entry->length != 64) { + fprintf(stderr, "unsupported entry ! version: %x with length: %d\n", + entry->version, entry->length); + return -EINVAL; + } + + sprintf(ptr, "%d", entry_count); + ptr += 10; + + timestamp = (le64_to_cpu(entry->power_on_hour) & 0x0000FFFFFFFFFFFFUL) / 1000; + hours = timestamp / 3600; + minutes = (timestamp % 3600) / 60; + seconds = (timestamp % 3600) % 60; + sprintf(ptr, "|%"PRIu64":%hhu:%hhu", (uint64_t)hours, minutes, seconds); + ptr += 12; + + sprintf(ptr, "| %"PRIu64, le64_to_cpu(entry->power_cycle_count)); + ptr += 10; + + /* firmware details */ + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, entry->previous_fw, sizeof(entry->previous_fw)); + sprintf(ptr, "| %s", buffer); + ptr += 11; + + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, entry->activated_fw, sizeof(entry->activated_fw)); + sprintf(ptr, "| %s", buffer); + ptr += 12; + + /* firmware slot and commit action*/ + sprintf(ptr, "| %d", entry->slot); + ptr += 9; + + if (entry->commit_action_type <= 3) + sprintf(ptr, "| %s", ca[entry->commit_action_type]); + else + sprintf(ptr, "| xxxb"); + ptr += 9; + + /* result */ + if (entry->result) { + sprintf(ptr, "| Fail #%d", entry->result); + } else { + sprintf(ptr, "| pass"); + } + + /* replace all null charecters with spaces */ + ptr = formatted_entry; + while (index < entry_size) { + if (ptr[index] == '\0') + ptr[index] = ' '; + index++; + } + return 0; +} + + +static int micron_fw_activation_history(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve Firmware Activation history of the given drive"; + char formatted_output[100]; + int count = 0; + unsigned int logC2[C2_log_size/sizeof(int)] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + int fd, err, ctrlIdx; + struct format { + char *fmt; + }; + + const char *fmt = "output format normal"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "normal") != 0) { + fprintf (stderr, "only normal format is supported currently\n"); + close(fd); + return -1; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) { + fprintf(stderr, "failed get device identification data, error: %x\n", err); + goto out; + } + + /* check if product supports fw_history log */ + err = -EINVAL; + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + eModel = GetDriveModel(ctrlIdx); + if (eModel != M51CX) { + fprintf(stderr, "Unsupported drive model for vs-fw-activate-history command\n"); + goto out; + } + + err = nvme_get_log(fd, NVME_NSID_ALL, 0xC2, false, NVME_NO_LOG_LSP, C2_log_size, logC2); + if (err) { + fprintf(stderr, "Failed to retrieve fw activation history log, error: %x\n", err); + goto out; + } + + /* check if we have atleast one entry to print */ + struct micron_fw_activation_history_table *table = + (struct micron_fw_activation_history_table *)logC2; + + /* check version and log page */ + if (table->version != 2 || table->log_page != 0xC2) { + fprintf(stderr, "Unsupported fw activation history page: %x, version: %x\n", + table->log_page, table->version); + goto out; + } + + if (table->num_entries == 0) { + fprintf(stderr, "No entries were found in fw activation history log\n"); + goto out; + } + + printf("%s", fw_activation_history_table_header); + for(count = 0; count < table->num_entries; count++) { + memset(formatted_output, '\0', 100); + if (display_fw_activate_entry(count, + &table->entries[count], + formatted_output, NULL) == 0) + { + printf("%s\n", formatted_output); + } + } +out: + if (fd > 0) + close(fd); + return err; +} + +static int micron_error_reason(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("This command is not implemented for the drive\n"); + return 0; +} + +static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve Micron OCP Smart Health log for the given device "; + unsigned int logC0[C0_log_size/sizeof(int)] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + int fd, err, ctrlIdx; + bool is_json = false; + struct format { + char *fmt; + }; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-smart-add-log commmand\n"); + close(fd); + goto out; + } + + /* should check for firmware version if this log is supported or not */ + if (eModel == M5407 || eModel == M5410) { + err = nvme_get_log(fd, NVME_NSID_ALL, 0xC0, false, NVME_NO_LOG_LSP, + C0_log_size, logC0); + } + if (err < 0) { + printf("Unable to retrieve extended smart log for the drive\n"); + err = -ENOTTY; + } else { + print_smart_cloud_health_log((__u8 *)logC0, is_json); + } +out: + close(fd); + return err; +} + +static int micron_clr_fw_activation_history(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { + const char *desc = "Clear FW activation history"; + int fd, err = 0; + __u32 result = 0; + __u8 fid = MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY; + eDriveModel model = UNKNOWN_MODEL; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + if (model != M51CX) { + printf ("This option is not supported for specified drive\n"); + close(fd); + return err; + } + + //err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + err = nvme_set_feature(fd, 1, fid, 0, 0, 0, 0, 0, &result); + if (err == 0) err = (int)result; + return err; +} - int err = 0; - int fd; - int ctrlIdx; - FILE *fpOutFile = NULL; - char strOSDirName[1024]; - char strCtrlDirName[1024]; - char strMainDirName[256]; - char tempFolder[PATH_MAX] = { 0 }; - unsigned int *puiIDDBuf; - unsigned int uiMask; - struct nvme_id_ctrl ctrl; - char sn[20] = { 0 }; - - struct { - unsigned char ucLogPage; - const char *strFileName; - int nLogSize; - int nMaxSize; - } aVendorLogs[32] = { - { 0xC1, "nvmelog_C1.bin", 0, 0 }, - { 0xC2, "nvmelog_C2.bin", 0, 0 }, - { 0xC4, "nvmelog_C4.bin", 0, 0 }, - { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, - { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, - { 0xE6, "nvmelog_E6.bin", 0, 0 }, - { 0xE7, "nvmelog_E7.bin", 0, 0 } - }, - aM51XXLogs[] = { - { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ - { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, - { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, - { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, - { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, - { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } - }, - aM51AXLogs[] = { - { 0xD0, "nvmelog_D0.bin", 512, 0 }, - { 0xCA, "nvmelog_CA.bin", 512, 0 }, - { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, - { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, - { 0x04, "changed_namespace_log.bin", 4096, 0 }, - { 0x05, "command_effects_log.bin", 4096, 0 }, - { 0x06, "drive_self_test.bin", 4096, 0 } - }, - aM51BXLogs[] = { - { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, - { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, - { 0xCA, "nvmelog_CA.bin", 512, 1024 } - }; - - eDriveModel eModel; - - const char *desc = "This retrieves the micron debug log package"; - const char *package = "Log output package name (required)"; - unsigned char *dataBuffer = NULL; - int bSize = 0; - int maxSize = 0; - - struct config { - char *package; - }; - - struct config cfg = { - .package = "" - }; - - OPT_ARGS(opts) = { - OPT_STRING("package", 'p', "FILE", &cfg.package, package), - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - - if (strlen(cfg.package) == 0) { - printf ("You must specify an output name for the log package. ie --p=logfiles.zip\n"); - goto out; - } - - if (fd < 0) - goto out; - - /* pull log details based on the model name */ - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { - printf ("Unsupported drive model for vs-internal-log collection\n"); - close(fd); - goto out; - } - - printf("Preparing log package. This will take a few seconds...\n"); - err = nvme_identify_ctrl(fd, &ctrl); - if (err) - goto out; - - // trim spaces out of serial number string */ - int i, j = 0; - for (i = 0; i < sizeof(ctrl.sn); i++) { - if (isblank(ctrl.sn[i])) - continue; - sn[j++] = ctrl.sn[i]; - } - sn[j] = '\0'; - strcpy(ctrl.sn, sn); - - SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); - - GetTimestampInfo(strOSDirName); - GetCtrlIDDInfo(strCtrlDirName, &ctrl); - GetOSConfig(strOSDirName); - GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); - - for (int i = 1; i <= ctrl.nn; i++) - GetNSIDDInfo(fd, strCtrlDirName, i); - - GetSmartlogData(fd, strCtrlDirName); - GetErrorlogData(fd, ctrl.elpe, strCtrlDirName); - - if (eModel != M5410) { - memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); - if (eModel == M51AX) - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs)); - else - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs)); - } - - for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && aVendorLogs[i].ucLogPage != 0; i++) { - err = -1; - switch (aVendorLogs[i].ucLogPage) { - case 0xC1: - case 0xC2: - case 0xC4: - err = GetLogPageSize(fd, aVendorLogs[i].ucLogPage, &bSize); - if (err == 0 && bSize > 0) - err = GetCommonLogPage(fd, aVendorLogs[i].ucLogPage, &dataBuffer, bSize); - break; - - case 0xE6: - case 0xE7: - puiIDDBuf = (unsigned int *)&ctrl; - uiMask = puiIDDBuf[1015]; - if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || (aVendorLogs[i].ucLogPage == 0xE7 - && uiMask == 1)) { - bSize = 0; - } else { - bSize = (int)puiIDDBuf[1015]; - if (bSize % (16 * 1024)) { - bSize += (16 * 1024) - (bSize % (16 * 1024)); - } - } - if (bSize != 0) { - dataBuffer = (unsigned char *)malloc(bSize); - memset(dataBuffer, 0, bSize); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - } - break; - - case 0xF7: - case 0xF9: - case 0xFC: - case 0xFD: - if (eModel == M51BX) - (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); - default: - bSize = aVendorLogs[i].nLogSize; - dataBuffer = (unsigned char *)malloc(bSize); - memset(dataBuffer, 0, bSize); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - maxSize = aVendorLogs[i].nMaxSize - bSize; - while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - aVendorLogs[i].strFileName); - fpOutFile = fopen(tempFolder, "ab+"); - if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) { - printf ("Unable to write log to file %s\n!", aVendorLogs[i].strFileName); - } - if (fpOutFile) - fclose(fpOutFile); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) - break; - maxSize -= bSize; - } - break; - } - - if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - aVendorLogs[i].strFileName); - fpOutFile = fopen(tempFolder, "ab+"); - if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) { - printf("Unable to write log to file %s\n!", aVendorLogs[i].strFileName); - } - if (fpOutFile) - fclose(fpOutFile); - } - - if (dataBuffer != NULL) { - free(dataBuffer); - dataBuffer = NULL; - } - } - - ZipAndRemoveDir(strMainDirName, cfg.package); - out: - return err; +static int micron_telemetry_cntrl_option(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + int err = 0; + __u32 result = 0; + const char *desc = "Enable or Disable Controller telemetry log generation"; + const char *option = "enable or disable or status"; + const char *select = "select/save values: enable/disable options" + "1 - save (persistent), 0 - non-persistent and for " + "status options: 0 - current, 1 - default, 2-saved"; + int fd = 0; + int fid = MICRON_FEATURE_TELEMETRY_CONTROL_OPTION; + eDriveModel model = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl = { 0 }; + + struct { + char *option; + int select; + } opt = { + .option = "disable", + .select= 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("select", 's', &opt.select, select), + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) { + return -1; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if ((ctrl.lpa & 0x8) != 0x8) { + printf("drive doesn't support host/controller generated telemetry logs\n"); + close(fd); + return err; + } + + if (!strcmp(opt.option, "enable")) { + err = nvme_set_feature(fd, 1, fid, 1, 0, (opt.select & 0x1), 0, 0, &result); + if (err == 0) { + printf("successfully set controller telemetry option\n"); + } else { + printf("Failed to set controller telemetry option\n"); + } + } else if (!strcmp(opt.option, "disable")) { + err = nvme_set_feature(fd, 1, fid, 0, 0, (opt.select & 0x1), 0, 0, &result); + if (err == 0) { + printf("successfully disabled controller telemetry option\n"); + } else { + printf("Failed to disable controller telemetry option\n"); + } + } else if (!strcmp(opt.option, "status")) { + opt.select &= 0x3; + err = nvme_get_feature(fd, 1, fid, opt.select, 0, 0, 0, &result); + if (err == 0) { + printf("Controller telemetry option : %s\n", + (result) ? "enabled" : "disabled"); + } else { + printf("Failed to retrieve controller telemetry option\n"); + } + } else { + printf("invalid option %s, valid values are enable,disable or status\n", opt.option); + close(fd); + return -1; + } + + close(fd); + return err; +} + +static int micron_internal_logs(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + int err = -EINVAL; + int fd = 0; + int ctrlIdx, telemetry_option = 0; + char strOSDirName[1024]; + char strCtrlDirName[1024]; + char strMainDirName[256]; + unsigned int *puiIDDBuf; + unsigned int uiMask; + struct nvme_id_ctrl ctrl; + char sn[20] = { 0 }; + char msg[256] = { 0 }; + + struct { + unsigned char ucLogPage; + const char *strFileName; + int nLogSize; + int nMaxSize; + } aVendorLogs[32] = { + { 0x03, "firmware_slot_info_log.bin", 512, 0 }, + { 0xC1, "nvmelog_C1.bin", 0, 0 }, + { 0xC2, "nvmelog_C2.bin", 0, 0 }, + { 0xC4, "nvmelog_C4.bin", 0, 0 }, + { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, + { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, + { 0xE6, "nvmelog_E6.bin", 0, 0 }, + { 0xE7, "nvmelog_E7.bin", 0, 0 } + }, + aM51XXLogs[] = { + { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ + { 0xD0, "nvmelog_D0.bin", 512, 0 }, + { 0x03, "firmware_slot_info_log.bin", 512, 0}, + { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, + { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, + { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, + { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, + { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } + }, + aM51AXLogs[] = { + { 0xCA, "nvmelog_CA.bin", 512, 0 }, + { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, + { 0xF6, "nvmelog_F6.bin", 4096, 512 * 1024 }, + { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, + { 0x04, "changed_namespace_log.bin", 4096, 0 }, + { 0x05, "command_effects_log.bin", 4096, 0 }, + { 0x06, "drive_self_test.bin", 4096, 0 } + }, + aM51BXLogs[] = { + { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, + { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, + { 0xCA, "nvmelog_CA.bin", 512, 1024 } + }; + + eDriveModel eModel; + + const char *desc = "This retrieves the micron debug log package"; + const char *package = "Log output data file name (required)"; + const char *type = "telemetry log type - host or controller"; + const char *data_area = "telemetry log data area 1, 2 or 3"; + unsigned char *dataBuffer = NULL; + int bSize = 0; + int maxSize = 0; + + struct config { + char *type; + char *package; + int data_area; + int log; + }; + + struct config cfg = { + .type = "", + .package = "", + .data_area = -1, + .log = 0x07, + }; + + OPT_ARGS(opts) = { + OPT_STRING("type", 't', "log type", &cfg.type, type), + OPT_STRING("package", 'p', "FILE", &cfg.package, package), + OPT_UINT("data_area", 'd', &cfg.data_area, data_area), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + goto out; + + /* if telemetry type is specified, check for data area */ + if (strlen(cfg.type) != 0) { + if (!strcmp(cfg.type, "controller")) { + cfg.log = 0x08; + } else if (strcmp(cfg.type, "host")) { + printf ("telemetry type (host or controller) should be specified i.e. -t=host\n"); + close(fd); + goto out; + } + + if (cfg.data_area <= 0 || cfg.data_area > 3) { + printf ("data area must be selected using -d option ie --d=1,2,3\n"); + close(fd); + goto out; + } + telemetry_option = 1; + } else if (cfg.data_area > 0) { + printf ("data area option is valid only for telemetry option (i.e --type=host|controller)\n"); + close(fd); + goto out; + } + + if (strlen(cfg.package) == 0) { + if (telemetry_option) + printf ("Log data file must be specified. ie -p=logfile.bin\n"); + else + printf ("Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz\n"); + goto out; + } + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-internal-log collection\n"); + close(fd); + goto out; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + err = -EINVAL; + if (telemetry_option) { + if ((ctrl.lpa & 0x8) != 0x8) { + printf("telemetry option is not supported for specified drive\n"); + close(fd); + goto out; + } + int logSize = 0; __u8 *buffer = NULL; const char *dir = "."; + err = micron_telemetry_log(fd, 0, cfg.log, &buffer, &logSize, cfg.data_area); + if (err == 0 && logSize > 0 && buffer != NULL) { + sprintf(msg, "telemetry log: 0x%X", cfg.log); + WriteData(buffer, logSize, dir, cfg.package, msg); + free(buffer); + } + close(fd); + goto out; + } + + printf("Preparing log package. This will take a few seconds...\n"); + + // trim spaces out of serial number string */ + int i, j = 0; + for (i = 0; i < sizeof(ctrl.sn); i++) { + if (isblank(ctrl.sn[i])) + continue; + sn[j++] = ctrl.sn[i]; + } + sn[j] = '\0'; + strcpy(ctrl.sn, sn); + + SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); + + GetTimestampInfo(strOSDirName); + GetCtrlIDDInfo(strCtrlDirName, &ctrl); + GetOSConfig(strOSDirName); + GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); + + for (int i = 1; i <= ctrl.nn; i++) + GetNSIDDInfo(fd, strCtrlDirName, i); + + GetSmartlogData(fd, strCtrlDirName); + GetErrorlogData(fd, ctrl.elpe, strCtrlDirName); + + // pull if telemetry log data is supported + if ((ctrl.lpa & 0x8) == 0x8) + GetTelemetryData(fd, strCtrlDirName); + + GetFeatureSettings(fd, strCtrlDirName); + + if (eModel != M5410) { + memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); + if (eModel == M51AX) + memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs)); + else + memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs)); + } + + for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && + aVendorLogs[i].ucLogPage != 0; i++) { + err = -1; + switch (aVendorLogs[i].ucLogPage) { + case 0xC1: + case 0xC2: + case 0xC4: + err = GetLogPageSize(fd, aVendorLogs[i].ucLogPage, &bSize); + if (err == 0 && bSize > 0) + err = GetCommonLogPage(fd, aVendorLogs[i].ucLogPage, &dataBuffer, bSize); + break; + + case 0xE6: + case 0xE7: + puiIDDBuf = (unsigned int *)&ctrl; + uiMask = puiIDDBuf[1015]; + if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || + (aVendorLogs[i].ucLogPage == 0xE7 && uiMask == 1)) { + bSize = 0; + } else { + bSize = (int)puiIDDBuf[1023]; + if (bSize % (16 * 1024)) { + bSize += (16 * 1024) - (bSize % (16 * 1024)); + } + } + if (bSize != 0 && (dataBuffer = (unsigned char *)malloc(bSize)) != NULL) { + memset(dataBuffer, 0, bSize); + if (eModel == M5410 || eModel == M5407) + err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); + else + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, + false, NVME_NO_LOG_LSP, bSize, dataBuffer); + } + break; + + case 0xF7: + case 0xF9: + case 0xFC: + case 0xFD: + if (eModel == M51BX) + (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, + aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); + default: + bSize = aVendorLogs[i].nLogSize; + dataBuffer = (unsigned char *)malloc(bSize); + if (dataBuffer == NULL) { + break; + } + memset(dataBuffer, 0, bSize); + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, + false, NVME_NO_LOG_LSP, bSize, dataBuffer); + maxSize = aVendorLogs[i].nMaxSize - bSize; + while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { + sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); + WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, + false, NVME_NO_LOG_LSP, bSize, dataBuffer); + if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) + break; + maxSize -= bSize; + } + break; + } + + if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { + sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); + WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); + } + + if (dataBuffer != NULL) { + free(dataBuffer); + dataBuffer = NULL; + } + } + + err = ZipAndRemoveDir(strMainDirName, cfg.package); +out: + return err; } diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h index add36e4..118f8cd 100644 --- a/plugins/micron/micron-nvme.h +++ b/plugins/micron/micron-nvme.h @@ -12,7 +12,17 @@ PLUGIN(NAME("micron", "Micron vendor specific extensions"), ENTRY("vs-pcie-stats", "Retrieve Micron PCIe error stats", micron_pcie_stats) ENTRY("clear-pcie-correctable-errors", "Clear correctable PCIe errors", micron_clear_pcie_correctable_errors) ENTRY("vs-internal-log", "Retrieve Micron logs", micron_internal_logs) + ENTRY("vs-telemetry-controller-option", "Enable/Disable controller telemetry log generation", micron_telemetry_cntrl_option) ENTRY("vs-nand-stats", "Retrieve NAND Stats", micron_nand_stats) + ENTRY("vs-drive-info", "Retrieve Drive information", micron_drive_info) + ENTRY("plugin-version", "Display plugin version info", micron_plugin_version) + ENTRY("cloud-SSD-plugin-version", "Display plugin version info", micron_cloud_ssd_plugin_version) + ENTRY("log-page-directory", "Retrieve log page directory", micron_logpage_dir) + ENTRY("vs-fw-activate-history", "Display FW activation history", micron_fw_activation_history) + ENTRY("vs-error-reason-identifier", "Retrieve Error reason", micron_error_reason) + ENTRY("vs-smart-add-log", "Retrieve extended SMART data", micron_ocp_smart_health_logs) + ENTRY("clear-fw-activate-history", "Clear FW activation history", micron_clr_fw_activation_history) + ENTRY("vs-smbus-option", "Enable/Disable SMBUS on the drive", micron_smbus_option) ) ); diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index a942f57..15c3923 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -25,7 +25,6 @@ #include "nvme.h" #include "nvme-ioctl.h" -#include "json.h" #include "suffix.h" @@ -197,7 +196,7 @@ static void netapp_get_ontap_labels(char *vsname, char *nspath, vol_name, "/", ns_name); } -static void netapp_smdevice_json(struct json_array *devices, char *devname, +static void netapp_smdevice_json(struct json_object *devices, char *devname, char *arrayname, char *volname, int nsid, char *nguid, char *ctrl, char *astate, char *size, long long lba, long long nsze) @@ -219,7 +218,7 @@ static void netapp_smdevice_json(struct json_array *devices, char *devname, json_array_add_value_object(devices, device_attrs); } -static void netapp_ontapdevice_json(struct json_array *devices, char *devname, +static void netapp_ontapdevice_json(struct json_object *devices, char *devname, char *vsname, char *nspath, int nsid, char *uuid, char *size, long long lba, long long nsze) { @@ -241,7 +240,7 @@ static void netapp_ontapdevice_json(struct json_array *devices, char *devname, static void netapp_smdevices_print(struct smdevice_info *devices, int count, int format) { struct json_object *root = NULL; - struct json_array *json_devices = NULL; + struct json_object *json_devices = NULL; int i, slta; char array_label[ARRAY_LABEL_LEN / 2 + 1]; char volume_label[VOLUME_LABEL_LEN / 2 + 1]; @@ -266,7 +265,7 @@ static void netapp_smdevices_print(struct smdevice_info *devices, int count, int else if (format == NJSON) { /* prepare for json output */ root = json_create_object(); - json_devices = json_create_array(); + json_devices = json_create_object(); } for (i = 0; i < count; i++) { @@ -304,7 +303,7 @@ static void netapp_ontapdevices_print(struct ontapdevice_info *devices, int count, int format) { struct json_object *root = NULL; - struct json_array *json_devices = NULL; + struct json_object *json_devices = NULL; char vsname[ONTAP_LABEL_LEN] = " "; char nspath[ONTAP_NS_PATHLEN] = " "; long long lba; @@ -332,7 +331,7 @@ static void netapp_ontapdevices_print(struct ontapdevice_info *devices, } else if (format == NJSON) { /* prepare for json output */ root = json_create_object(); - json_devices = json_create_array(); + json_devices = json_create_object(); } for (i = 0; i < count; i++) { diff --git a/plugins/nvidia/nvidia-nvme.c b/plugins/nvidia/nvidia-nvme.c new file mode 100644 index 0000000..cdf51ab --- /dev/null +++ b/plugins/nvidia/nvidia-nvme.c @@ -0,0 +1,58 @@ +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include "linux/nvme_ioctl.h" + +#include "common.h" +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "plugin.h" + +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include "nvidia-nvme.h" + +struct nvme_vu_id_ctrl_field { + __u16 json_rpc_2_0_mjr; + __u16 json_rpc_2_0_mnr; + __u16 json_rpc_2_0_ter; + __u8 reserved0[1018]; +}; + +static void json_nvidia_id_ctrl(struct nvme_vu_id_ctrl_field *id, + char *json_rpc_2_0_ver, struct json_object *root) +{ + json_object_add_value_string(root, "json_rpc_2_0_ver", + json_rpc_2_0_ver); +} + +static void nvidia_id_ctrl(__u8 *vs, struct json_object *root) +{ + struct nvme_vu_id_ctrl_field *id = (struct nvme_vu_id_ctrl_field *)vs; + char json_rpc_2_0_ver[16] = { 0 }; + + snprintf(json_rpc_2_0_ver, sizeof(json_rpc_2_0_ver), "0x%04x%04x%04x", + le16_to_cpu(id->json_rpc_2_0_mjr), + le16_to_cpu(id->json_rpc_2_0_mnr), + le16_to_cpu(id->json_rpc_2_0_ter)); + + if (root) { + json_nvidia_id_ctrl(id, json_rpc_2_0_ver, root); + return; + } + + printf("json_rpc_2_0_ver : %s\n", json_rpc_2_0_ver); +} + +static int id_ctrl(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return __id_ctrl(argc, argv, cmd, plugin, nvidia_id_ctrl); +} diff --git a/plugins/nvidia/nvidia-nvme.h b/plugins/nvidia/nvidia-nvme.h new file mode 100644 index 0000000..bf562b9 --- /dev/null +++ b/plugins/nvidia/nvidia-nvme.h @@ -0,0 +1,17 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/nvidia/nvidia-nvme + +#if !defined(NVIDIA_NVME) || defined(CMD_HEADER_MULTI_READ) +#define NVIDIA_NVME + +#include "cmd.h" + +PLUGIN(NAME("nvidia", "NVIDIA vendor specific extensions"), + COMMAND_LIST( + ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index 846ca77..df7f9a3 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -8,6 +8,8 @@ #include <asm/byteorder.h> #include <sys/ioctl.h> #include <sys/sysinfo.h> +#include <sys/stat.h> +#include <unistd.h> #include "linux/nvme_ioctl.h" @@ -15,7 +17,6 @@ #include "nvme-print.h" #include "nvme-ioctl.h" #include "nvme-status.h" -#include "json.h" #include "plugin.h" #include "argconfig.h" @@ -28,36 +29,40 @@ #define SECTOR_SHIFT 9 #define SFX_GET_FREESPACE _IOWR('N', 0x240, struct sfx_freespace_ctx) -#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL) +#define NVME_IOCTL_CLR_CARD _IO('N', 0x47) +#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL) +#define IDEMA_CAP2GB(exp_sector) (((__u64)exp_sector - 97696368ULL) / 1953504ULL + 50ULL) enum { SFX_LOG_LATENCY_READ_STATS = 0xc1, SFX_LOG_SMART = 0xc2, - SFX_LOG_LATENCY_WRITE_STATS = 0xc3, + SFX_LOG_LATENCY_WRITE_STATS = 0xc3, SFX_LOG_QUAL = 0xc4, SFX_LOG_MISMATCHLBA = 0xc5, SFX_LOG_MEDIA = 0xc6, SFX_LOG_BBT = 0xc7, SFX_LOG_IDENTIFY = 0xcc, SFX_FEAT_ATOMIC = 0x01, + SFX_FEAT_UP_P_CAP = 0xac, + SFX_FEAT_CLR_CARD = 0xdc, }; enum sfx_nvme_admin_opcode { nvme_admin_query_cap_info = 0xd3, nvme_admin_change_cap = 0xd4, - nvme_admin_sfx_set_features = 0xd5, - nvme_admin_sfx_get_features = 0xd6, + nvme_admin_sfx_set_features = 0xd5, + nvme_admin_sfx_get_features = 0xd6, }; struct sfx_freespace_ctx { __u64 free_space; - __u64 phy_cap; /* physical capacity, in unit of sector */ - __u64 phy_space; /* physical space considering OP, in unit of sector */ - __u64 user_space; /* user required space, in unit of sector*/ - __u64 hw_used; /* hw space used in 4K */ - __u64 app_written; /* app data written in 4K */ + __u64 phy_cap; /* physical capacity, in unit of sector */ + __u64 phy_space; /* physical space considering OP, in unit of sector */ + __u64 user_space; /* user required space, in unit of sector*/ + __u64 hw_used; /* hw space used in 4K */ + __u64 app_written; /* app data written in 4K */ }; struct nvme_capacity_info { @@ -66,7 +71,7 @@ struct nvme_capacity_info { __u64 used_space; __u64 free_space; }; -struct __attribute__((packed)) nvme_additional_smart_log_item { +struct __attribute__((packed)) nvme_additional_smart_log_item { uint8_t key; uint8_t _kp[2]; uint8_t norm; @@ -112,11 +117,10 @@ int nvme_change_cap(int fd, __u32 nsid, __u64 capacity) struct nvme_admin_cmd cmd = { .opcode = nvme_admin_change_cap, .nsid = nsid, - .cdw10 = (capacity & 0xffffffff), - .cdw11 = (capacity >> 32), + .cdw10 = (capacity & 0xffffffff), + .cdw11 = (capacity >> 32), }; - return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd); } @@ -267,65 +271,65 @@ static void show_sfx_smart_log(struct nvme_additional_smart_log *smart, unsigned int nsid, const char *devname) { printf("Additional Smart Log for ScaleFlux device:%s namespace-id:%x\n", - devname, nsid); - printf("key normalized raw\n"); - printf("program_fail_count : %3d%% %"PRIu64"\n", - smart->program_fail_cnt.norm, - int48_to_long(smart->program_fail_cnt.raw)); - printf("erase_fail_count : %3d%% %"PRIu64"\n", - smart->erase_fail_cnt.norm, - int48_to_long(smart->erase_fail_cnt.raw)); - printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", - smart->wear_leveling_cnt.norm, - le16_to_cpu(smart->wear_leveling_cnt.wear_level.min), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.max), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg)); - printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", - smart->e2e_err_cnt.norm, - int48_to_long(smart->e2e_err_cnt.raw)); - printf("crc_error_count : %3d%% %"PRIu64"\n", - smart->crc_err_cnt.norm, - int48_to_long(smart->crc_err_cnt.raw)); - printf("timed_workload_media_wear : %3d%% %.3f%%\n", - smart->timed_workload_media_wear.norm, - ((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); - printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n", - smart->timed_workload_host_reads.norm, - int48_to_long(smart->timed_workload_host_reads.raw)); - printf("timed_workload_timer : %3d%% %"PRIu64" min\n", - smart->timed_workload_timer.norm, - int48_to_long(smart->timed_workload_timer.raw)); - printf("thermal_throttle_status : %3d%% %u%%, cnt: %u\n", - smart->thermal_throttle_status.norm, - smart->thermal_throttle_status.thermal_throttle.pct, - smart->thermal_throttle_status.thermal_throttle.count); - printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n", - smart->retry_buffer_overflow_cnt.norm, - int48_to_long(smart->retry_buffer_overflow_cnt.raw)); - printf("pll_lock_loss_count : %3d%% %"PRIu64"\n", - smart->pll_lock_loss_cnt.norm, - int48_to_long(smart->pll_lock_loss_cnt.raw)); - printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->nand_bytes_written.norm, - int48_to_long(smart->nand_bytes_written.raw)); - printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->host_bytes_written.norm, - int48_to_long(smart->host_bytes_written.raw)); - printf("raid_recover_cnt : %3d%% %"PRIu64"\n", - smart->raid_recover_cnt.norm, - int48_to_long(smart->raid_recover_cnt.raw)); - printf("read_ecc_cnt : %3d%% %"PRIu64"\n", - smart->read_ecc_cnt.norm, - int48_to_long(smart->read_ecc_cnt.raw)); - printf("prog_timeout_cnt : %3d%% %"PRIu64"\n", - smart->prog_timeout_cnt.norm, - int48_to_long(smart->prog_timeout_cnt.raw)); - printf("erase_timeout_cnt : %3d%% %"PRIu64"\n", - smart->erase_timeout_cnt.norm, - int48_to_long(smart->erase_timeout_cnt.raw)); - printf("read_timeout_cnt : %3d%% %"PRIu64"\n", - smart->read_timeout_cnt.norm, - int48_to_long(smart->read_timeout_cnt.raw)); + devname, nsid); + printf("key normalized raw\n"); + printf("program_fail_count : %3d%% %"PRIu64"\n", + smart->program_fail_cnt.norm, + int48_to_long(smart->program_fail_cnt.raw)); + printf("erase_fail_count : %3d%% %"PRIu64"\n", + smart->erase_fail_cnt.norm, + int48_to_long(smart->erase_fail_cnt.raw)); + printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", + smart->wear_leveling_cnt.norm, + le16_to_cpu(smart->wear_leveling_cnt.wear_level.min), + le16_to_cpu(smart->wear_leveling_cnt.wear_level.max), + le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg)); + printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", + smart->e2e_err_cnt.norm, + int48_to_long(smart->e2e_err_cnt.raw)); + printf("crc_error_count : %3d%% %"PRIu64"\n", + smart->crc_err_cnt.norm, + int48_to_long(smart->crc_err_cnt.raw)); + printf("timed_workload_media_wear : %3d%% %.3f%%\n", + smart->timed_workload_media_wear.norm, + ((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); + printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n", + smart->timed_workload_host_reads.norm, + int48_to_long(smart->timed_workload_host_reads.raw)); + printf("timed_workload_timer : %3d%% %"PRIu64" min\n", + smart->timed_workload_timer.norm, + int48_to_long(smart->timed_workload_timer.raw)); + printf("thermal_throttle_status : %3d%% %u%%, cnt: %u\n", + smart->thermal_throttle_status.norm, + smart->thermal_throttle_status.thermal_throttle.pct, + smart->thermal_throttle_status.thermal_throttle.count); + printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n", + smart->retry_buffer_overflow_cnt.norm, + int48_to_long(smart->retry_buffer_overflow_cnt.raw)); + printf("pll_lock_loss_count : %3d%% %"PRIu64"\n", + smart->pll_lock_loss_cnt.norm, + int48_to_long(smart->pll_lock_loss_cnt.raw)); + printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", + smart->nand_bytes_written.norm, + int48_to_long(smart->nand_bytes_written.raw)); + printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", + smart->host_bytes_written.norm, + int48_to_long(smart->host_bytes_written.raw)); + printf("raid_recover_cnt : %3d%% %"PRIu64"\n", + smart->raid_recover_cnt.norm, + int48_to_long(smart->raid_recover_cnt.raw)); + printf("read_ecc_cnt : %3d%% %"PRIu64"\n", + smart->read_ecc_cnt.norm, + int48_to_long(smart->read_ecc_cnt.raw)); + printf("prog_timeout_cnt : %3d%% %"PRIu64"\n", + smart->prog_timeout_cnt.norm, + int48_to_long(smart->prog_timeout_cnt.raw)); + printf("erase_timeout_cnt : %3d%% %"PRIu64"\n", + smart->erase_timeout_cnt.norm, + int48_to_long(smart->erase_timeout_cnt.raw)); + printf("read_timeout_cnt : %3d%% %"PRIu64"\n", + smart->read_timeout_cnt.norm, + int48_to_long(smart->read_timeout_cnt.raw)); } static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -357,8 +361,8 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, fd = parse_and_open(argc, argv, desc, opts); - err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, sizeof(smart_log), - (void *)&smart_log); + err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, NVME_NO_LOG_LSP, + sizeof(smart_log), (void *)&smart_log); if (!err) { if (cfg.json) show_sfx_smart_log_jsn(&smart_log, cfg.namespace_id, devicename); @@ -373,7 +377,6 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, return err; } - struct sfx_lat_stats { __u16 maj; __u16 min; @@ -440,7 +443,8 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct fd = parse_and_open(argc, argv, desc, opts); - err = nvme_get_log(fd, 0xffffffff, cfg.write ? 0xc3 : 0xc1, false, sizeof(stats), (void *)&stats); + err = nvme_get_log(fd, 0xffffffff, cfg.write ? 0xc3 : 0xc1, false, NVME_NO_LOG_LSP, + sizeof(stats), (void *)&stats); if (!err) { if (!cfg.raw_binary) show_lat_stats(&stats, cfg.write); @@ -448,7 +452,7 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct d_raw((unsigned char *)&stats, sizeof(stats)); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", - nvme_status_to_string(err), err); + nvme_status_to_string(err), err); return err; } @@ -517,16 +521,16 @@ static void bd_table_show(unsigned char *bd_table, __u64 table_size) bb_elem = (__u64 *)(bd_table + 5 * sizeof(__u32)); printf("Bad Block Table \n"); - printf("MF_BB_COUNT: %u\n", mf_bb_count); - printf("GROWN_BB_COUNT: %u\n", grown_bb_count); - printf("TOTAL_BB_COUNT: %u\n", total_bb_count); - printf("REMAP_MFBB_COUNT: %u\n", remap_mfbb_count); - printf("REMAP_GBB_COUNT: %u\n", remap_gbb_count); + printf("MF_BB_COUNT: %u\n", mf_bb_count); + printf("GROWN_BB_COUNT: %u\n", grown_bb_count); + printf("TOTAL_BB_COUNT: %u\n", total_bb_count); + printf("REMAP_MFBB_COUNT: %u\n", remap_mfbb_count); + printf("REMAP_GBB_COUNT: %u\n", remap_gbb_count); printf("REMAP_MFBB_TABLE ["); i = 0; while (bb_elem < elem_end && i < remap_mfbb_count) { - printf(" 0x%llx", *(bb_elem++)); + printf(" 0x%"PRIx64"", (uint64_t)*(bb_elem++)); i++; } printf(" ]\n"); @@ -534,14 +538,14 @@ static void bd_table_show(unsigned char *bd_table, __u64 table_size) printf("REMAP_GBB_TABLE ["); i = 0; while (bb_elem < elem_end && i < remap_gbb_count) { - printf(" 0x%llx",*(bb_elem++)); + printf(" 0x%"PRIx64"", (uint64_t)*(bb_elem++)); i++; } printf(" ]\n"); } /** - * @brief "hooks of sfx get-bad-block" + * @brief "hooks of sfx get-bad-block" * * @param argc * @param argv @@ -592,10 +596,16 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct static void show_cap_info(struct sfx_freespace_ctx *ctx) { - printf("user sectors: %#llx\n", ctx->user_space); - printf("totl physical sectors: %#llx\n", ctx->phy_space); - printf("free physical sectors: %#llx\n", ctx->free_space); - printf("used physical sectors: %#llx\n", ctx->phy_space - ctx->free_space); + + printf("logic capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->user_space), (uint64_t)ctx->user_space); + printf("provisioned capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->phy_space), (uint64_t)ctx->phy_space); + printf("free provisioned capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->free_space), (uint64_t)ctx->free_space); + printf("used provisioned capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->phy_space) - IDEMA_CAP2GB(ctx->free_space), + (uint64_t)(ctx->phy_space - ctx->free_space)); } static int query_cap_info(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -631,42 +641,94 @@ static int query_cap_info(int argc, char **argv, struct command *cmd, struct plu return err; } -static int change_cap_mem_check(int fd, __u64 trg_in_4k) +static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink) { struct sfx_freespace_ctx freespace_ctx = { 0 }; struct sysinfo s_info; __u64 mem_need = 0; __u64 cur_in_4k = 0; + __u64 provisoned_cap_4k = 0; __u32 cnt_ms = 0; + int extend = 0; while (ioctl(fd, SFX_GET_FREESPACE, &freespace_ctx)) { if (cnt_ms++ > 600) {//1min - fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno); return -1; } usleep(100000); } - cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT); - if (cur_in_4k > trg_in_4k) { - return 0; + /* + * capacity illegal check + */ + provisoned_cap_4k = freespace_ctx.phy_space >> + (SFX_PAGE_SHIFT - SECTOR_SHIFT); + 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"); + if (trg_in_4k < provisoned_cap_4k) { + fprintf(stderr, + "WARNING: The target capacity is less than 1.0 x provisioned capacity!\n"); + } else { + fprintf(stderr, + "WARNING: The target capacity is larger than 4.0 x provisioned capacity!\n"); + } + return -1; } - - if (sysinfo(&s_info) < 0) { - printf("change-cap query mem info fail\n"); + if (trg_in_4k > ((__u64)provisoned_cap_4k*4)) { + fprintf(stderr, "WARNING: the target capacity is too large\n"); return -1; } - mem_need = (trg_in_4k - cur_in_4k) * 8; - if (s_info.freeram <= 10 || mem_need > s_info.freeram) { - fprintf(stderr, "WARNING: mem needed is %llu, free mem is %lu\n" - "Insufficient memory, please drop cache or add free memory and retry\n", - mem_need, s_info.freeram); - return -1; + /* + * check whether mem enough if extend + * */ + cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT); + extend = (cur_in_4k <= trg_in_4k); + if (extend) { + if (sysinfo(&s_info) < 0) { + printf("change-cap query mem info fail\n"); + return -1; + } + mem_need = (trg_in_4k - cur_in_4k) * 8; + if (s_info.freeram <= 10 || mem_need > s_info.freeram) { + fprintf(stderr, + "WARNING: Free memory is not enough! " + "Please drop cache or extend more memory and retry\n" + "WARNING: Memory needed is %"PRIu64", free memory is %"PRIu64"\n", + (uint64_t)mem_need, (uint64_t)s_info.freeram); + return -1; + } } + *shrink = !extend; + return 0; } +/** + * @brief prompt and get user confirm input + * + * @param str, prompt string + * + * @return 0, cancled; 1 confirmed + */ +static int sfx_confirm_change(const char *str) +{ + char confirm; + fprintf(stderr, "WARNING: %s.\n" + "Use the force [--force] option to suppress this warning.\n", str); + + fprintf(stderr, "Confirm Y/y, Others cancel:\n"); + confirm = fgetc(stdin); + if (confirm != 'y' && confirm != 'Y') { + fprintf(stderr, "Cancled.\n"); + return 0; + } + fprintf(stderr, "Sending operation ... \n"); + return 1; +} + static int change_cap(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = -1, fd; @@ -678,6 +740,8 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; __u64 cap_in_4k = 0; __u64 cap_in_sec = 0; + int shrink = 0; + struct config { __u64 cap_in_byte; __u32 capacity_in_gb; @@ -694,7 +758,7 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin OPT_ARGS(opts) = { OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb), - OPT_UINT("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), + OPT_SUFFIX("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), OPT_FLAG("force", 'f', &cfg.force, force), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG("json", 'j', &cfg.json, json), @@ -706,22 +770,21 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin return fd; } - if (!cfg.force) { - fprintf(stderr, "WARNING: Changing capacity may irrevocably delete user data.\n" - "You have 10 seconds to press Ctrl-C to cancel this operation.\n\n" - "Use the force [--force|-f] option to suppress this warning.\n"); - sleep(10); - fprintf(stderr, "Sending operation ... \n"); - } - cap_in_sec = IDEMA_CAP(cfg.capacity_in_gb); cap_in_4k = cap_in_sec >> 3; if (cfg.cap_in_byte) cap_in_4k = cfg.cap_in_byte >> 12; - printf("%dG %lluB %llu 4K\n", - cfg.capacity_in_gb, cfg.cap_in_byte, cap_in_4k); - if (change_cap_mem_check(fd, cap_in_4k)) + printf("%dG %"PRIu64"B %"PRIu64" 4K\n", + cfg.capacity_in_gb, (uint64_t)cfg.cap_in_byte, (uint64_t)cap_in_4k); + + if (change_sanity_check(fd, cap_in_4k, &shrink)) { + printf("ScaleFlux change-capacity: fail\n"); return err; + } + + if (!cfg.force && shrink && !sfx_confirm_change("Changing Cap may irrevocably delete this device's data")) { + return 0; + } err = nvme_change_cap(fd, 0xffffffff, cap_in_4k); if (err < 0) @@ -731,20 +794,54 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin nvme_status_to_string(err), err); else { printf("ScaleFlux change-capacity: success\n"); - if(ioctl(fd, BLKRRPART) < 0) { - fprintf(stderr, "failed to re-read partition table\n"); - err = EFAULT; - } + ioctl(fd, BLKRRPART); } return err; } +static int sfx_verify_chr(int fd) +{ + static struct stat nvme_stat; + int err = fstat(fd, &nvme_stat); + + if (err < 0) { + perror("fstat"); + return errno; + } + if (!S_ISCHR(nvme_stat.st_mode)) { + fprintf(stderr, + "Error: requesting clean card on non-controller handle\n"); + return ENOTBLK; + } + return 0; +} + +static int sfx_clean_card(int fd) +{ + int ret; + + ret = sfx_verify_chr(fd); + if (ret) + return ret; + ret = ioctl(fd, NVME_IOCTL_CLR_CARD); + if (ret) + perror("Ioctl Fail."); + else + printf("ScaleFlux clean card success\n"); + + return ret; +} + char *sfx_feature_to_string(int feature) { switch (feature) { - case SFX_FEAT_ATOMIC: return "ATOMIC"; + case SFX_FEAT_ATOMIC: + return "ATOMIC"; + case SFX_FEAT_UP_P_CAP: + return "UPDATE_PROVISION_CAPACITY"; - default: return "Unknown"; + default: + return "Unknown"; } } @@ -752,27 +849,34 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl { int err = 0, fd; char *desc = "ScaleFlux internal set features\n" - "feature id 1: ATOMIC"; + "feature id 1: ATOMIC\n" + "value 0: Disable atomic write\n" + " 1: Enable atomic write"; const char *value = "new value of feature (required)"; const char *feature_id = "hex feature name (required)"; const char *namespace_id = "desired namespace"; + const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; + struct nvme_id_ns ns; struct config { __u32 namespace_id; __u32 feature_id; __u32 value; + __u32 force; }; struct config cfg = { .namespace_id = 1, .feature_id = 0, .value = 0, + .force = 0, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'v', &cfg.value, value), + OPT_UINT("value", 'v', &cfg.value, value), + OPT_FLAG("force", 's', &cfg.force, force), OPT_END() }; @@ -786,7 +890,17 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl return EINVAL; } - if (cfg.feature_id == SFX_FEAT_ATOMIC) { + if (cfg.feature_id == SFX_FEAT_CLR_CARD) { + /*Warning for clean card*/ + if (!cfg.force && !sfx_confirm_change("Going to clean device's data, confirm umount fs and try again")) { + return 0; + } else { + return sfx_clean_card(fd); + } + + } + + if (cfg.feature_id == SFX_FEAT_ATOMIC && cfg.value != 0) { if (cfg.namespace_id != 0xffffffff) { err = nvme_identify_ns(fd, cfg.namespace_id, 0, &ns); if (err) { @@ -806,14 +920,25 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl return EFAULT; } } + } else if (cfg.feature_id == SFX_FEAT_UP_P_CAP) { + if (cfg.value <= 0) { + fprintf(stderr, "Invalid Param\n"); + return EINVAL; + } + + /*Warning for change pacp by GB*/ + if (!cfg.force && !sfx_confirm_change("Changing physical capacity may irrevocably delete this device's data")) { + return 0; + } } err = nvme_sfx_set_features(fd, cfg.namespace_id, cfg.feature_id, cfg.value); + if (err < 0) { perror("ScaleFlux-set-feature"); return errno; } else if (!err) { - printf("ScaleFlux set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, + printf("ScaleFlux set-feature:%#02x (%s), value:%d\n", cfg.feature_id, sfx_feature_to_string(cfg.feature_id), cfg.value); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", diff --git a/plugins/scaleflux/sfx-nvme.h b/plugins/scaleflux/sfx-nvme.h index daf9c33..8f14501 100644 --- a/plugins/scaleflux/sfx-nvme.h +++ b/plugins/scaleflux/sfx-nvme.h @@ -14,12 +14,10 @@ PLUGIN(NAME("sfx", "ScaleFlux vendor specific extensions"), ENTRY("query-cap", "Query current capacity info", query_cap_info) ENTRY("change-cap", "Dynamic change capacity", change_cap) ENTRY("set-feature", "Set a feature", sfx_set_feature) - ENTRY("get-feature", "get a feature", sfx_get_feature) + ENTRY("get-feature", "Get a feature", sfx_get_feature) ) ); #endif #include "define_cmd.h" - - diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 7ba14f8..d5ef32b 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -36,7 +36,6 @@ #include "plugin.h" #include "argconfig.h" #include "suffix.h" -#include "json.h" #define CREATE_CMD @@ -132,7 +131,7 @@ static char *log_pages_supp_print(__u32 pageID) static void json_log_pages_supp(log_page_map *logPageMap) { struct json_object *root; - struct json_array *logPages; + struct json_object *logPages; __u32 i = 0; root = json_create_object(); @@ -177,7 +176,8 @@ static int log_pages_supp(int argc, char **argv, struct command *cmd, }; fd = parse_and_open(argc, argv, desc, opts); - err = nvme_get_log(fd, 1, 0xc5, false, sizeof(logPageMap), &logPageMap); + err = nvme_get_log(fd, 1, 0xc5, false, NVME_NO_LOG_LSP, + sizeof(logPageMap), &logPageMap); if (!err) { if (strcmp(cfg.output_format,"json")) { printf ("Seagate Supported Log-pages count :%d\n", @@ -312,7 +312,7 @@ static char *print_ext_smart_id(__u8 attrId) return "RAIS_ECC_CORRECT_ERR_COUNT"; break; case VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS: - return "Uncorrectable read error count";/*VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS*/ + return "Uncorrectable RAISE error count";/*VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS*/ break; case VS_ATTR_ID_DRIVE_LIFE_PROTECTION_STATUS: return "DRIVE_LIFE_PROTECTION_STATUS"; @@ -493,7 +493,7 @@ static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T *ExtdSMARTInfo ) { /*struct json_object *root; */ - struct json_array *lbafs; + struct json_object *lbafs; int index = 0; static __u64 lsbGbErased = 0, msbGbErased = 0, lsbLifWrtToFlash = 0, msbLifWrtToFlash = 0, @@ -650,7 +650,7 @@ static void json_print_smart_log_CF(struct json_object *root, vendor_log_page_CF *pLogPageCF) { /*struct json_object *root;*/ - struct json_array *logPages; + struct json_object *logPages; unsigned int currentTemp, maxTemp; char buf[40]; @@ -715,11 +715,9 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi EXTENDED_SMART_INFO_T ExtdSMARTInfo; vendor_log_page_CF logPageCF; int fd; - struct json_object *root; - struct json_array *lbafs; + struct json_object *root = json_create_object(); + struct json_object *lbafs = json_create_array(); struct json_object *lbafs_ExtSmart, *lbafs_DramSmart; - root = json_create_object(); - lbafs = json_create_array(); const char *desc = "Retrieve Seagate Extended SMART information for the given device "; const char *output_format = "output in binary format"; @@ -741,7 +739,8 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi if (strcmp(cfg.output_format,"json")) printf("Seagate Extended SMART Information :\n"); - err = nvme_get_log(fd, 1, 0xC4, false, sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); + err = nvme_get_log(fd, 1, 0xC4, false, NVME_NO_LOG_LSP, + sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); if (!err) { if (strcmp(cfg.output_format,"json")) { printf("%-39s %-15s %-19s \n", "Description", "Ext-Smart-Id", "Ext-Smart-Value"); @@ -763,7 +762,8 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi * Next get Log Page 0xCF */ - err = nvme_get_log(fd, 1, 0xCF, false, sizeof(logPageCF), &logPageCF); + err = nvme_get_log(fd, 1, 0xCF, false, NVME_NO_LOG_LSP, + sizeof(logPageCF), &logPageCF); if (!err) { if(strcmp(cfg.output_format,"json")) { /*printf("Seagate DRAM Supercap SMART Attributes :\n");*/ @@ -860,7 +860,8 @@ static int temp_stats(int argc, char **argv, struct command *cmd, struct plugin } /* STEP-2 : Get Max temperature form Ext SMART-id 194 */ - err = nvme_get_log(fd, 1, 0xC4, false, sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); + err = nvme_get_log(fd, 1, 0xC4, false, NVME_NO_LOG_LSP, + sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); if (!err) { for(index = 0; index < NUMBER_EXTENDED_SMART_ATTRIBUTES; index++) { if (ExtdSMARTInfo.vendorData[index].AttributeNumber == VS_ATTR_ID_MAX_LIFE_TEMPERATURE) { @@ -882,7 +883,8 @@ static int temp_stats(int argc, char **argv, struct command *cmd, struct plugin fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - cf_err = nvme_get_log(fd, 1, 0xCF, false, sizeof(ExtdSMARTInfo), &logPageCF); + cf_err = nvme_get_log(fd, 1, 0xCF, false, NVME_NO_LOG_LSP, + sizeof(ExtdSMARTInfo), &logPageCF); if(!cf_err) { scCurrentTemp = logPageCF.AttrCF.SuperCapCurrentTemperature; @@ -1011,7 +1013,8 @@ static int vs_pcie_error_log(int argc, char **argv, struct command *cmd, struct if(strcmp(cfg.output_format,"json")) printf("Seagate PCIe error counters Information :\n"); - err = nvme_get_log(fd, 1, 0xCB, false, sizeof(pcieErrorLog), &pcieErrorLog); + err = nvme_get_log(fd, 1, 0xCB, false, NVME_NO_LOG_LSP, + sizeof(pcieErrorLog), &pcieErrorLog); if (!err) { if(strcmp(cfg.output_format,"json")) { print_vs_pcie_error_log(pcieErrorLog); diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index 3aa6f8a..588f4f3 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -11,7 +11,6 @@ #include "nvme.h" #include "nvme-print.h" #include "nvme-ioctl.h" -#include "json.h" #include "plugin.h" #include "argconfig.h" @@ -143,7 +142,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, fd = parse_and_open(argc, argv, desc, opts); err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, - sizeof(smart_log), &smart_log); + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); if (!err) { if (!cfg.raw_binary) show_shannon_smart_log(&smart_log, cfg.namespace_id, devicename); @@ -182,7 +181,7 @@ static int get_additional_feature(int argc, char **argv, struct command *cmd, st struct config { __u32 namespace_id; - __u32 feature_id; + enum nvme_feat feature_id; __u8 sel; __u32 cdw11; __u32 data_len; @@ -192,7 +191,7 @@ static int get_additional_feature(int argc, char **argv, struct command *cmd, st struct config cfg = { .namespace_id = 1, - .feature_id = 0, + .feature_id = NVME_FEAT_NONE, .sel = 0, .cdw11 = 0, .data_len = 0, diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index c067ce8..53d54bc 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -393,7 +393,7 @@ static int nvme_get_vendor_log(int fd, __u32 namespace_id, int log_page, goto end; } err = nvme_get_log(fd, namespace_id, log_page, false, - log_len, log); + NVME_NO_LOG_LSP, log_len, log); if (err) { fprintf(stderr, "%s: couldn't get log 0x%x\n", __func__, log_page); diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index 47b0fdc..a194a5f 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -633,7 +633,7 @@ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) const char *VWCtable[2] = {"0 = a volatile write cache is not present", "1 = a volatile write cache is present"}; - const char *NVSCCtable[2] = {"0 = the format of all NVM Vendor Specific Commands are vendor specific", + const char *ICSVSCCtable[2] = {"0 = the format of all NVM Vendor Specific Commands are vendor specific", "1 = all NVM Vendor Specific Commands use the format defined in NVM Express specification"}; const char *SGLSSubtable[4] = {"00b = SGLs are not supported", @@ -883,11 +883,11 @@ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) vt_convert_data_buffer_to_hex_string(&buf[528], 2, true, s); printf(" \"Atomic Write Unit Power Fail\":\"%sh\",\n", s); - temp = ctrl->nvscc; + temp = ctrl->icsvscc; printf(" \"NVM Vendor Specific Command Configuration\":{\n"); vt_convert_data_buffer_to_hex_string(&buf[530], 1, true, s); printf(" \"Value\":\"%sh\",\n", s); - vt_build_identify_lv2(temp, 0, 1, NVSCCtable, true); + vt_build_identify_lv2(temp, 0, 1, ICSVSCCtable, true); vt_convert_data_buffer_to_hex_string(&buf[532], 2, true, s); printf(" \"Atomic Compare 0 Write Unit\":\"%sh\",\n", s); diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 0cebe3f..f7a5b31 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -19,6 +19,7 @@ * Author: Chaitanya Kulkarni <chaitanya.kulkarni@hgst.com>, * Dong Ho <dong.ho@hgst.com>, * Jeff Lien <jeff.lien@wdc.com> + * Brandon Paupore <brandon.paupore@wdc.com> */ #include <stdio.h> #include <string.h> @@ -36,7 +37,7 @@ #include "nvme-print.h" #include "nvme-ioctl.h" #include "plugin.h" -#include "json.h" +#include "nvme-status.h" #include "argconfig.h" #include "suffix.h" @@ -52,6 +53,11 @@ #define WDC_NVME_LOG_SIZE_DATA_LEN 0x08 #define WDC_NVME_LOG_SIZE_HDR_LEN 0x08 +/* Enclosure */ +#define WDC_OPENFLEX_MI_DEVICE_MODEL "OpenFlex" +#define WDC_RESULT_MORE_DATA 0x80000000 +#define WDC_RESULT_NOT_AVAILABLE 0x7FFFFFFF + /* Device Config */ #define WDC_NVME_VID 0x1c58 #define WDC_NVME_VID_2 0x1b96 @@ -66,12 +72,13 @@ #define WDC_NVME_SN640_DEV_ID 0x2400 #define WDC_NVME_SN640_DEV_ID_1 0x2401 #define WDC_NVME_SN640_DEV_ID_2 0x2402 -#define WDC_NVME_SN640_DEV_ID_3 0x2404 -#define WDC_NVME_ZN440_DEV_ID 0x2600 -#define WDC_NVME_SN440_DEV_ID 0x2610 -#define WDC_NVME_SN7GC_DEV_ID 0x2700 -#define WDC_NVME_SN7GC_DEV_ID_1 0x2701 -#define WDC_NVME_SN7GC_DEV_ID_2 0x2702 +#define WDC_NVME_SN640_DEV_ID_3 0x2404 +#define WDC_NVME_ZN540_DEV_ID 0x2600 +#define WDC_NVME_SN540_DEV_ID 0x2610 +#define WDC_NVME_SN650_DEV_ID 0x2700 +#define WDC_NVME_SN650_DEV_ID_1 0x2701 +#define WDC_NVME_SN650_DEV_ID_2 0x2702 +#define WDC_NVME_SN650_DEV_ID_3 0x2720 #define WDC_NVME_SXSLCL_DEV_ID 0x2001 #define WDC_NVME_SN520_DEV_ID 0x5003 #define WDC_NVME_SN520_DEV_ID_1 0x5004 @@ -81,6 +88,8 @@ #define WDC_NVME_SN730B_DEV_ID 0x3714 #define WDC_NVME_SN730B_DEV_ID_1 0x3734 #define WDC_NVME_SN340_DEV_ID 0x500d +#define WDC_NVME_ZN350_DEV_ID 0x5010 +#define WDC_NVME_ZN350_DEV_ID_1 0x5018 #define WDC_DRIVE_CAP_CAP_DIAG 0x0000000000000001 #define WDC_DRIVE_CAP_INTERNAL_LOG 0x0000000000000002 @@ -92,9 +101,9 @@ #define WDC_DRIVE_CAP_CLEAR_PCIE 0x0000000000000080 #define WDC_DRIVE_CAP_RESIZE 0x0000000000000100 #define WDC_DRIVE_CAP_NAND_STATS 0x0000000000000200 -#define WDC_DRIVE_CAP_DRIVE_LOG 0x0000000000000400 -#define WDC_DRIVE_CAP_CRASH_DUMP 0x0000000000000800 -#define WDC_DRIVE_CAP_PFAIL_DUMP 0x0000000000001000 +#define WDC_DRIVE_CAP_DRIVE_LOG 0x0000000000000400 +#define WDC_DRIVE_CAP_CRASH_DUMP 0x0000000000000800 +#define WDC_DRIVE_CAP_PFAIL_DUMP 0x0000000000001000 #define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY 0x0000000000002000 #define WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY 0x0000000000004000 #define WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG 0x0000000000008000 @@ -102,14 +111,31 @@ #define WDC_DRIVE_CAP_LOG_PAGE_DIR 0x0000000000020000 #define WDC_DRIVE_CAP_NS_RESIZE 0x0000000000040000 #define WDC_DRIVE_CAP_INFO 0x0000000000080000 +#define WDC_DRIVE_CAP_C0_LOG_PAGE 0x0000000000100000 +#define WDC_DRIVE_CAP_TEMP_STATS 0x0000000000200000 +#define WDC_DRIVE_CAP_VUC_CLEAR_PCIE 0x0000000000400000 +#define WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE 0x0000000000800000 +#define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 0x0000000001000000 +#define WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY 0x0000000002000000 +#define WDC_DRIVE_CAP_CLOUD_SSD_VERSION 0x0000000004000000 +#define WDC_DRIVE_CAP_PCIE_STATS 0x0000000008000000 +#define WDC_DRIVE_CAP_INFO_2 0x0000000010000000 #define WDC_DRIVE_CAP_DRIVE_ESSENTIALS 0x0000000100000000 #define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000 #define WDC_SN730B_CAP_VUC_LOG 0x0000000400000000 -#define WDC_DRIVE_CAP_SN340_DUI 0x0000000800000000 -#define WDC_DRIVE_CAP_SMART_LOG_MASK (WDC_DRIVE_CAP_C1_LOG_PAGE | WDC_DRIVE_CAP_CA_LOG_PAGE | \ - WDC_DRIVE_CAP_D0_LOG_PAGE) - +#define WDC_DRIVE_CAP_DUI 0x0000000800000000 +#define WDC_DRIVE_CAP_SMART_LOG_MASK (WDC_DRIVE_CAP_C0_LOG_PAGE | WDC_DRIVE_CAP_C1_LOG_PAGE | \ + WDC_DRIVE_CAP_CA_LOG_PAGE | WDC_DRIVE_CAP_D0_LOG_PAGE) +#define WDC_DRIVE_CAP_CLEAR_PCIE_MASK (WDC_DRIVE_CAP_CLEAR_PCIE | \ + WDC_DRIVE_CAP_VUC_CLEAR_PCIE | \ + WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE) +#define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_MASK (WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | \ + WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2) +#define WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY_MASK (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | \ + WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY) +#define WDC_DRIVE_CAP_INTERNAL_LOG_MASK (WDC_DRIVE_CAP_INTERNAL_LOG | 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 @@ -126,9 +152,20 @@ /* Customer ID's */ #define WDC_CUSTOMER_ID_GN 0x0001 #define WDC_CUSTOMER_ID_GD 0x0101 -#define WDC_CUSTOMER_ID_0x1004 0x1004 +#define WDC_CUSTOMER_ID_BD 0x1009 + #define WDC_CUSTOMER_ID_0x1005 0x1005 +#define WDC_CUSTOMER_ID_0x1004 0x1004 +#define WDC_CUSTOMER_ID_0x1008 0x1008 +#define WDC_CUSTOMER_ID_0x1304 0x1304 + +#define WDC_ALL_PAGE_MASK 0xFFFF +#define WDC_C0_PAGE_MASK 0x0001 +#define WDC_C1_PAGE_MASK 0x0002 +#define WDC_CA_PAGE_MASK 0x0004 +#define WDC_D0_PAGE_MASK 0x0008 + /* Drive Resize */ #define WDC_NVME_DRIVE_RESIZE_OPCODE 0xCC #define WDC_NVME_DRIVE_RESIZE_CMD 0x03 @@ -142,6 +179,9 @@ #define WDC_NVME_DRIVE_INFO_CMD 0x22 #define WDC_NVME_DRIVE_INFO_SUBCMD 0x06 +/* VS PCIE Stats */ +#define WDC_NVME_PCIE_STATS_OPCODE 0xD1 + /* Capture Diagnostics */ #define WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE WDC_NVME_LOG_SIZE_DATA_LEN #define WDC_NVME_CAP_DIAG_OPCODE 0xE6 @@ -160,6 +200,7 @@ #define WDC_NVME_DUI_MAX_SECTION_V2 0x26 #define WDC_NVME_DUI_MAX_SECTION_V3 0x23 #define WDC_NVME_DUI_MAX_DATA_AREA 0x05 +#define WDC_NVME_SN730_SECTOR_SIZE 512 /* Telemtery types for vs-internal-log command */ #define WDC_TELEMETRY_TYPE_NONE 0x0 @@ -224,6 +265,7 @@ #define WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE 0xC6 #define WDC_NVME_CLEAR_FW_ACT_HIST_CMD 0x23 #define WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD 0x05 +#define WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID 0xC1 /* Additional Smart Log */ #define WDC_ADD_LOG_BUF_LEN 0x4000 @@ -233,6 +275,7 @@ /* 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_C2_LOG_BUF_LEN 0x1000 #define WDC_C2_LOG_PAGES_SUPPORTED_ID 0x08 #define WDC_C2_CUSTOMER_ID_ID 0x15 @@ -262,11 +305,20 @@ /* C0 EOL Status Log Page */ #define WDC_NVME_GET_EOL_STATUS_LOG_OPCODE 0xC0 #define WDC_NVME_EOL_STATUS_LOG_LEN 0x200 +#define WDC_NVME_SMART_CLOUD_ATTR_LEN 0x200 + +/* C0 SMART Cloud Attributes Log Page*/ +#define WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE 0xC0 /* CB - FW Activate History Log Page */ #define WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID 0xCB #define WDC_FW_ACT_HISTORY_LOG_BUF_LEN 0x3d0 +/* C2 - FW Activation History Log Page */ +#define WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID 0xC2 +#define WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN 0x1000 +#define WDC_MAX_NUM_ACT_HIST_ENTRIES 20 + /* D0 Smart Log Page */ #define WDC_NVME_GET_VU_SMART_LOG_OPCODE 0xD0 #define WDC_NVME_VU_SMART_LOG_LEN 0x200 @@ -274,13 +326,16 @@ /* Log Page Directory defines */ #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_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_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 +#define WDC_LOG_ID_D1 0xD1 #define WDC_LOG_ID_D6 0xD6 #define WDC_LOG_ID_D7 0xD7 #define WDC_LOG_ID_D8 0xD8 @@ -291,10 +346,11 @@ #define WDC_LOG_ID_FA 0xFA /* Clear PCIe Correctable Errors */ -#define WDC_NVME_CLEAR_PCIE_CORR_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE -#define WDC_NVME_CLEAR_PCIE_CORR_CMD 0x22 -#define WDC_NVME_CLEAR_PCIE_CORR_SUBCMD 0x04 - +#define WDC_NVME_CLEAR_PCIE_CORR_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE +#define WDC_NVME_CLEAR_PCIE_CORR_CMD 0x22 +#define WDC_NVME_CLEAR_PCIE_CORR_SUBCMD 0x04 +#define WDC_NVME_CLEAR_PCIE_CORR_OPCODE_VUC 0xD2 +#define WDC_NVME_CLEAR_PCIE_CORR_FEATURE_ID 0xC3 /* Clear Assert Dump Status */ #define WDC_NVME_CLEAR_ASSERT_DUMP_OPCODE 0xD8 #define WDC_NVME_CLEAR_ASSERT_DUMP_CMD 0x03 @@ -319,6 +375,8 @@ /* VU Opcodes */ #define WDC_DE_VU_READ_SIZE_OPCODE 0xC0 #define WDC_DE_VU_READ_BUFFER_OPCODE 0xC2 +#define WDC_NVME_ADMIN_ENC_MGMT_SND 0xC9 +#define WDC_NVME_ADMIN_ENC_MGMT_RCV 0xCA #define WDC_DE_FILE_HEADER_SIZE 4 #define WDC_DE_FILE_OFFSET_SIZE 2 @@ -332,6 +390,21 @@ #define WDC_DE_DESTN_SPI 1 #define WDC_DE_DUMPTRACE_DESTINATION 6 +#define NVME_ID_CTRL_MODEL_NUMBER_SIZE 40 +#define NVME_ID_CTRL_SERIAL_NUMBER_SIZE 20 + +/* Enclosure log */ +#define WDC_NVME_ENC_LOG_SIZE_CHUNK 0x1000 +#define WDC_NVME_ENC_NIC_LOG_SIZE 0x400000 + +/* Enclosure nic crash dump get-log id */ +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_1 0xD1 +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_2 0xD2 +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_3 0xD3 +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_4 0xD4 +#define WDC_ENC_CRASH_DUMP_ID 0xE4 +#define WDC_ENC_LOG_DUMP_ID 0xE2 + typedef enum _NVME_FEATURES_SELECT { FS_CURRENT = 0, @@ -392,6 +465,62 @@ typedef enum WDC_DE_TYPE_ALL = 0xFFFFFFF, } WDC_DRIVE_ESSENTIAL_TYPE; +typedef enum +{ + SCAO_PMUW = 0, /* Physical media units written */ + SCAO_PMUR = 16, /* Physical media units read */ + SCAO_BUNBR = 32, /* Bad user nand blocks raw */ + SCAO_BUNBN = 38, /* Bad user nand blocks normalized */ + SCAO_BSNBR = 40, /* Bad system nand blocks raw */ + SCAO_BSNBN = 46, /* Bad system nand blocks normalized */ + SCAO_XRC = 48, /* XOR recovery count */ + SCAO_UREC = 56, /* Uncorrectable read error count */ + SCAO_SEEC = 64, /* Soft ecc error count */ + SCAO_EECE = 72, /* End to end corrected errors */ + SCAO_EEDC = 76, /* End to end detected errors */ + SCAO_SDPU = 80, /* System data percent used */ + SCAO_RFSC = 81, /* Refresh counts */ + SCAO_MXUDEC = 88, /* Max User data erase counts */ + SCAO_MNUDEC = 92, /* Min User data erase counts */ + SCAO_NTTE = 96, /* Number of Thermal throttling events */ + SCAO_CTS = 97, /* Current throttling status */ + SCAO_EVF = 98, /* Errata Version Field */ + SCAO_PVF = 99, /* Point Version Field */ + SCAO_MIVF = 101, /* Minor Version Field */ + SCAO_MAVF = 103, /* Major Version Field */ + SCAO_PCEC = 104, /* PCIe correctable error count */ + SCAO_ICS = 112, /* Incomplete shutdowns */ + SCAO_PFB = 120, /* Percent free blocks */ + SCAO_CPH = 128, /* Capacitor health */ + SCAO_NEV = 130, /* NVMe Errata Version */ + SCAO_UIO = 136, /* Unaligned I/O */ + SCAO_SVN = 144, /* Security Version Number */ + SCAO_NUSE = 152, /* NUSE - Namespace utilization */ + SCAO_PSC = 160, /* PLP start count */ + SCAO_EEST = 176, /* Endurance estimate */ + SCAO_PLRC = 192, /* PCIe Link Retraining Count */ + SCAO_LPV = 494, /* Log page version */ + SCAO_LPG = 496, /* Log page GUID */ +} SMART_CLOUD_ATTRIBUTE_OFFSETS; + +#define WDC_C2_GUID_LENGTH 16 + +static __u8 scao_guid[WDC_C2_GUID_LENGTH] = { 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF, 0xF2, 0xA4, + 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF }; + +typedef enum +{ + EOL_RBC = 76, /* Realloc Block Count */ + EOL_ECCR = 80, /* ECC Rate */ + EOL_WRA = 84, /* Write Amp */ + EOL_PLR = 88, /* Percent Life Remaining */ + EOL_RSVBC = 92, /* Reserved Block Count */ + EOL_PFC = 96, /* Program Fail Count */ + EOL_EFC = 100, /* Erase Fail Count */ + EOL_RRER = 108, /* Raw Read Error Rate */ +} EOL_LOG_PAGE_C0_OFFSETS; + + typedef struct __attribute__((__packed__)) _WDC_DE_VU_FILE_META_DATA { __u8 fileName[WDC_DE_FILE_NAME_SIZE]; @@ -517,6 +646,12 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command static int wdc_do_drive_info(int fd, __u32 *result); static int wdc_vs_drive_info(int argc, char **argv, struct command *command, struct plugin *plugin); +static int wdc_vs_temperature_stats(int argc, char **argv, struct command *command, + struct plugin *plugin); +static __u64 wdc_get_enc_drive_capabilities(int fd); +static int wdc_enc_get_nic_log(int fd, __u8 log_id, __u32 xfer_size, __u32 data_len, FILE *out); +static int wdc_enc_submit_move_data(int fd, char *cmd, int len, int xfer_size, FILE *out, int data_id, int cdw14, int cdw15); +static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data); /* Drive log data size */ struct wdc_log_size { @@ -543,6 +678,14 @@ struct __attribute__((__packed__)) wdc_dui_log_section_v2 { __le64 section_size; }; +/* DUI log header V4 */ +struct wdc_dui_log_section_v4 { + __le16 section_type; + __u8 data_area_id; + __u8 reserved; + __le32 section_size_sectors; +}; + struct wdc_dui_log_hdr { __u8 telemetry_hdr[512]; __le16 hdr_version; @@ -573,6 +716,16 @@ struct __attribute__((__packed__)) wdc_dui_log_hdr_v3 { __u8 log_data[40]; }; +struct __attribute__((__packed__)) wdc_dui_log_hdr_v4 { + __u8 telemetry_hdr[512]; + __u8 hdr_version; + __u8 product_id; + __le16 section_count; + __le32 log_size_sectors; + struct wdc_dui_log_section_v4 log_section[WDC_NVME_DUI_MAX_SECTION]; + __u8 log_data[40]; +}; + /* Purge monitor response */ struct wdc_nvme_purge_monitor_data { __le16 rsvd1; @@ -706,7 +859,57 @@ struct __attribute__((__packed__)) wdc_nand_stats { __le64 nand_rec_trigger_event; __le64 e2e_error_counter; __le64 successful_ns_resize_event; - __u8 rsvd[444]; + __u8 rsvd[442]; + __u16 log_page_version; +}; + +struct __attribute__((__packed__)) wdc_nand_stats_V3 { + __u8 nand_write_tlc[16]; + __u8 nand_write_slc[16]; + __u8 bad_nand_block_count[8]; + __le64 xor_recovery_count; + __le64 uecc_read_error_count; + __u8 ssd_correction_counts[16]; + __u8 percent_life_used; + __le64 user_data_erase_counts[4]; + __u8 program_fail_count[8]; + __u8 erase_fail_count[8]; + __le64 correctable_error_count; + __u8 percent_free_blocks_user; + __le64 security_version_number; + __u8 percent_free_blocks_system; + __u8 trim_completions[25]; + __u8 back_pressure_guage; + __le64 soft_ecc_error_count; + __le64 refresh_count; + __u8 bad_sys_nand_block_count[8]; + __u8 endurance_estimate[16]; + __u8 thermal_throttling_st_ct[2]; + __le64 unaligned_IO; + __u8 physical_media_units[16]; + __u8 reserved[279]; + __u16 log_page_version; +}; + +struct wdc_vs_pcie_stats +{ + __le64 unsupportedRequestErrorCount; + __le64 ecrcErrorStatusCount; + __le64 malformedTlpStatusCount; + __le64 receiverOverflowStatusCount; + __le64 unexpectedCmpltnStatusCount; + __le64 completeAbortStatusCount; + __le64 cmpltnTimoutStatusCount; + __le64 flowControlErrorStatusCount; + __le64 poisonedTlpStatusCount; + __le64 dLinkPrtclErrorStatusCount; + __le64 advsryNFatalErrStatusCount; + __le64 replayTimerToStatusCount; + __le64 replayNumRolloverStCount; + __le64 badDllpStatusCount; + __le64 badTlpStatusCount; + __le64 receiverErrStatusCount; + __u8 reserved1[384]; }; struct wdc_fw_act_history_log_hdr { @@ -731,6 +934,32 @@ struct wdc_fw_act_history_log_entry { __u8 reserved[12]; }; +struct __attribute__((__packed__)) wdc_fw_act_history_log_entry_c2 { + __u8 entry_version_num; + __u8 entry_len; + __le16 reserved; + __le16 fw_act_hist_entries; + __le64 timestamp; + __u8 reserved2[8]; + __le64 power_cycle_count; + __le64 previous_fw_version; + __le64 current_fw_version; + __u8 slot_number; + __u8 commit_action_type; + __le16 result; + __u8 reserved3[14]; +}; + +struct __attribute__((__packed__)) wdc_fw_act_history_log_format_c2 { + __u8 log_identifier; + __u8 reserved[3]; + __le32 num_entries; + struct wdc_fw_act_history_log_entry_c2 entry[20]; + __u8 reserved2[2790]; + __le16 log_page_version; + __u8 log_page_guid[WDC_C2_GUID_LENGTH]; +}; + #define WDC_REASON_INDEX_MAX 16 #define WDC_REASON_ID_ENTRY_LEN 128 #define WDC_REASON_ID_PATH_NAME "/usr/local/nvmecli" @@ -835,15 +1064,63 @@ free_id: return ret; } +static int wdc_get_vendor_id(int fd, uint32_t *vendor_id) +{ + int ret; + struct nvme_id_ctrl ctrl; + + memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); + ret = nvme_identify_ctrl(fd, &ctrl); + if (ret) { + fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed " + "0x%x\n", ret); + return -1; + } + + *vendor_id = (uint32_t) ctrl.vid; + + return ret; +} + +static bool wdc_check_power_of_2(int num) +{ + return (num && ( !(num & (num-1)))); +} + +static int wdc_get_model_number(int fd, char *model) +{ + int ret,i; + struct nvme_id_ctrl ctrl; + + memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); + ret = nvme_identify_ctrl(fd, &ctrl); + if (ret) { + fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed " + "0x%x\n", ret); + return -1; + } + + memcpy(model,ctrl.mn,NVME_ID_CTRL_MODEL_NUMBER_SIZE); + /* get rid of the padded spaces */ + i = NVME_ID_CTRL_MODEL_NUMBER_SIZE-1; + while (model[i] == ' ') i--; + model[i+1]=0; + + return ret; +} + static bool wdc_check_device(int fd) { int ret; bool supported; - uint32_t read_device_id, read_vendor_id; + uint32_t read_device_id = -1, read_vendor_id = -1; ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id); - if (ret < 0) - return false; + if (ret < 0) { + /* Use the identify nvme command to get vendor id due to NVMeOF device. */ + if (wdc_get_vendor_id(fd, &read_vendor_id) < 0) + return false; + } supported = false; @@ -858,14 +1135,46 @@ static bool wdc_check_device(int fd) return supported; } +static bool wdc_enc_check_model(int fd) +{ + int ret; + bool supported; + char model[NVME_ID_CTRL_MODEL_NUMBER_SIZE+1]; + + ret = wdc_get_model_number(fd, model); + if (ret < 0) + return false; + + supported = false; + model[NVME_ID_CTRL_MODEL_NUMBER_SIZE] = 0; /* forced termination */ + if (strstr(model,WDC_OPENFLEX_MI_DEVICE_MODEL) != NULL) + supported = true; + else + fprintf(stderr, "ERROR : WDC: unsupported WDC enclosure, Model = %s\n",model); + + return supported; +} + static __u64 wdc_get_drive_capabilities(int fd) { int ret; - uint32_t read_device_id, read_vendor_id; + uint32_t read_device_id = -1, read_vendor_id = -1; __u64 capabilities = 0; + __u8 *data; + __u32 *cust_id; ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id); if (ret < 0) + { + if (wdc_get_vendor_id(fd, &read_vendor_id) < 0) + return capabilities; + } + + /* below check condition is added due in NVMeOF device we dont have device_id so we need to use only vendor_id*/ + if (read_device_id == -1 && read_vendor_id != -1) + { + capabilities = wdc_get_enc_drive_capabilities(fd); return capabilities; + } switch (read_vendor_id) { case WDC_NVME_VID: @@ -912,25 +1221,59 @@ static __u64 wdc_get_drive_capabilities(int fd) { /* FALLTHRU */ case WDC_NVME_SN640_DEV_ID_2: /* FALLTHRU */ - case WDC_NVME_SN640_DEV_ID_3: - /* FALLTHRU */ + case WDC_NVME_SN640_DEV_ID_3: + /* verify the 0xC0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { + capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; + } + + capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | + WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | + WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID | + WDC_DRIVE_CAP_LOG_PAGE_DIR); + + /* verify the 0xCA log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE; + + /* verify the 0xD0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE; + + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + return -1; + } + + cust_id = (__u32*)data; + + if ((*cust_id == WDC_CUSTOMER_ID_0x1004) || (*cust_id == WDC_CUSTOMER_ID_0x1008) || + (*cust_id == WDC_CUSTOMER_ID_0x1005) || (*cust_id == WDC_CUSTOMER_ID_0x1304)) + capabilities |= (WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | + WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_CLOUD_SSD_VERSION); + else + capabilities |= (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_CLEAR_PCIE); + + break; case WDC_NVME_SN840_DEV_ID: /* FALLTHRU */ case WDC_NVME_SN840_DEV_ID_1: + /* verify the 0xC0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { + capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; + } /* FALLTHRU */ - case WDC_NVME_ZN440_DEV_ID: + case WDC_NVME_ZN540_DEV_ID: /* FALLTHRU */ - case WDC_NVME_SN440_DEV_ID: - /* FALLTHRU */ - case WDC_NVME_SN7GC_DEV_ID: - case WDC_NVME_SN7GC_DEV_ID_1: - case WDC_NVME_SN7GC_DEV_ID_2: - capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + case WDC_NVME_SN540_DEV_ID: + /* FALLTHRU */ + capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID | - WDC_DRIVE_CAP_LOG_PAGE_DIR | WDC_DRIVE_CAP_INFO); + WDC_DRIVE_CAP_LOG_PAGE_DIR ); /* verify the 0xCA log page is supported */ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) @@ -940,6 +1283,24 @@ static __u64 wdc_get_drive_capabilities(int fd) { if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true) capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE; break; + case WDC_NVME_SN650_DEV_ID: + case WDC_NVME_SN650_DEV_ID_1: + case WDC_NVME_SN650_DEV_ID_2: + case WDC_NVME_SN650_DEV_ID_3: + /* verify the 0xC0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { + capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; + } + + capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | + WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | + WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | + WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID | + WDC_DRIVE_CAP_LOG_PAGE_DIR | WDC_DRIVE_CAP_INFO | + WDC_DRIVE_CAP_CLOUD_SSD_VERSION); + + break; case WDC_NVME_SN730B_DEV_ID: /* FALLTHRU */ case WDC_NVME_SN730B_DEV_ID_1: @@ -965,10 +1326,18 @@ static __u64 wdc_get_drive_capabilities(int fd) { capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_NS_RESIZE; break; case WDC_NVME_SN730A_DEV_ID: - capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_NAND_STATS; + capabilities = WDC_DRIVE_CAP_DUI | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_INFO_2 + | WDC_DRIVE_CAP_TEMP_STATS | WDC_DRIVE_CAP_VUC_CLEAR_PCIE | WDC_DRIVE_CAP_PCIE_STATS; break; case WDC_NVME_SN340_DEV_ID: - capabilities = WDC_DRIVE_CAP_SN340_DUI; + capabilities = WDC_DRIVE_CAP_DUI; + break; + case WDC_NVME_ZN350_DEV_ID: + /* FALLTHRU */ + case WDC_NVME_ZN350_DEV_ID_1: + capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_C0_LOG_PAGE | + WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 | + WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_CLOUD_SSD_VERSION | WDC_DRIVE_CAP_LOG_PAGE_DIR; break; default: capabilities = 0; @@ -981,6 +1350,71 @@ static __u64 wdc_get_drive_capabilities(int fd) { return capabilities; } +static __u64 wdc_get_enc_drive_capabilities(int fd) { + int ret; + uint32_t read_vendor_id; + __u64 capabilities = 0; + __u8 *data; + __u32 *cust_id; + + ret = wdc_get_vendor_id(fd, &read_vendor_id); + if (ret < 0) + return capabilities; + + switch (read_vendor_id) { + case WDC_NVME_VID: + capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_CLEAR_PCIE | + WDC_DRIVE_CAP_DRIVE_LOG | WDC_DRIVE_CAP_CRASH_DUMP | WDC_DRIVE_CAP_PFAIL_DUMP); + + /* verify the 0xCA log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE; + + /* verify the 0xC1 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_ADD_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_C1_LOG_PAGE; + break; + case WDC_NVME_VID_2: + capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | + WDC_DRIVE_CAP_RESIZE); + + /* verify the 0xCB log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID) == true) + capabilities |= WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY; + + /* verify the 0xCA log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE; + + /* verify the 0xD0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE; + + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + return -1; + } + + cust_id = (__u32*)data; + + if ((*cust_id == WDC_CUSTOMER_ID_0x1004) || (*cust_id == WDC_CUSTOMER_ID_0x1008) || + (*cust_id == WDC_CUSTOMER_ID_0x1005) || (*cust_id == WDC_CUSTOMER_ID_0x1304)) + capabilities |= (WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE); + else + capabilities |= (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_CLEAR_PCIE); + + break; + case WDC_NVME_SNDK_VID: + capabilities = WDC_DRIVE_CAP_DRIVE_ESSENTIALS; + break; + default: + capabilities = 0; + } + + return capabilities; +} + static int wdc_get_serial_name(int fd, char *file, size_t len, const char *suffix) { int i; @@ -1060,6 +1494,99 @@ static int wdc_create_log_file(char *file, __u8 *drive_log_data, return 0; } +bool wdc_get_dev_mng_log_entry(__u32 log_length, + __u32 entry_id, + struct wdc_c2_log_page_header* p_log_hdr, + struct wdc_c2_log_subpage_header **p_p_found_log_entry) +{ + __u32 remaining_len = 0; + __u32 log_entry_hdr_size = sizeof(struct wdc_c2_log_subpage_header) - 1; + __u32 log_entry_size = 0; + __u32 size = 0; + bool valid_log; + __u32 current_data_offset = 0; + struct wdc_c2_log_subpage_header *p_next_log_entry = NULL; + + if (*p_p_found_log_entry == NULL) { + fprintf(stderr, "ERROR : WDC - wdc_get_dev_mng_log_entry: No ppLogEntry pointer.\n"); + return false; + } + + *p_p_found_log_entry = NULL; + + /* Ensure log data is large enough for common header */ + if (log_length < sizeof(struct wdc_c2_log_page_header)) { + fprintf(stderr, "ERROR : WDC - wdc_get_dev_mng_log_entry: \ + Buffer is not large enough for the common header. BufSize: 0x%x HdrSize: %"PRIxPTR"\n", + log_length, sizeof(struct wdc_c2_log_page_header)); + return false; + } + + /* Get pointer to first log Entry */ + size = sizeof(struct wdc_c2_log_page_header); + current_data_offset = size; + p_next_log_entry = (struct wdc_c2_log_subpage_header *)((__u8*)p_log_hdr + current_data_offset); + remaining_len = log_length - size; + valid_log = false; + + /* Walk the entire structure. Perform a sanity check to make sure this is a + standard version of the structure. This means making sure each entry looks + valid. But allow for the data to overflow the allocated + buffer (we don't want a false negative because of a FW formatting error) */ + + /* Proceed only if there is at least enough data to read an entry header */ + while (remaining_len >= log_entry_hdr_size) { + /* Get size of the next entry */ + log_entry_size = p_next_log_entry->length; + + /* If log entry size is 0 or the log entry goes past the end + of the data, we must be at the end of the data */ + if ((log_entry_size == 0) || + (log_entry_size > remaining_len)) { + fprintf(stderr, "ERROR : WDC: wdc_get_dev_mng_log_entry: \ + Detected unaligned end of the data. Data Offset: 0x%x \ + Entry Size: 0x%x, Remaining Log Length: 0x%x Entry Id: 0x%x\n", + current_data_offset, log_entry_size, remaining_len, p_next_log_entry->entry_id); + + /* Force the loop to end */ + remaining_len = 0; + } else if ((p_next_log_entry->entry_id == 0) || + (p_next_log_entry->entry_id > 200)) { + /* Invalid entry - fail the search */ + fprintf(stderr, "ERROR : WDC: wdc_get_dev_mng_log_entry: \ + Invalid entry found at offset: 0x%x Entry Size: 0x%x, \ + Remaining Log Length: 0x%x Entry Id: 0x%x\n", + current_data_offset, log_entry_size, remaining_len, p_next_log_entry->entry_id); + + /* Force the loop to end */ + remaining_len = 0; + valid_log = false; + + /* The struture 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 */ + valid_log = true; + if (p_next_log_entry->entry_id == entry_id) { + /* A potential match. */ + *p_p_found_log_entry = p_next_log_entry; + } + + remaining_len -= log_entry_size; + + if (remaining_len > 0) { + /* Increment the offset counter */ + current_data_offset += log_entry_size; + + /* Get the next entry */ + p_next_log_entry = (struct wdc_c2_log_subpage_header *)(((__u8*)p_log_hdr) + current_data_offset); + } + } + } + + return valid_log; +} + static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) { int ret = -1; @@ -1068,8 +1595,17 @@ static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) struct wdc_c2_log_subpage_header *sph; __u32 length = 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(&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; if ((data = (__u8*) malloc(sizeof (__u8) * WDC_C2_LOG_BUF_LEN)) == NULL) { fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); @@ -1078,10 +1614,9 @@ static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) memset(data, 0, sizeof (__u8) * WDC_C2_LOG_BUF_LEN); /* get the log page length */ - ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE, - false, WDC_C2_LOG_BUF_LEN, data); + ret = nvme_get_log14(fd, 0xFFFFFFFF, lid, NVME_NO_LOG_LSP, 0, 0, false, uuid_ix, WDC_C2_LOG_BUF_LEN, data); if (ret) { - fprintf(stderr, "ERROR : WDC : Unable to get C2 Log Page length, ret = 0x%x\n", ret); + fprintf(stderr, "ERROR : WDC : Unable to get 0x%x Log Page length, ret = 0x%x\n", lid, ret); goto end; } @@ -1097,28 +1632,37 @@ static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) } } - ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE, - false, le32_to_cpu(hdr_ptr->length), data); - /* parse the data until the List of log page ID's is found */ + /* get the log page data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, lid, NVME_NO_LOG_LSP, 0, 0, false, uuid_ix, le32_to_cpu(hdr_ptr->length), data); if (ret) { - fprintf(stderr, "ERROR : WDC : Unable to read C2 Log Page data, ret = 0x%x\n", ret); + fprintf(stderr, "ERROR : WDC : Unable to read 0x%x Log Page data, ret = 0x%x\n", lid, 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); + found = wdc_get_dev_mng_log_entry(hdr_ptr->length, log_id, hdr_ptr, &sph); - while (length < le32_to_cpu(hdr_ptr->length)) { + if (found) { + *cbs_data = (void *)&sph->data; + } else { + /* not found with uuid = 1 try with uuid = 0 */ + uuid_ix = 0; + /* get the log page data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, lid, NVME_NO_LOG_LSP, 0, 0, false, uuid_ix, le32_to_cpu(hdr_ptr->length), data); + hdr_ptr = (struct wdc_c2_log_page_header *)data; sph = (struct wdc_c2_log_subpage_header *)(data + length); - - if (le32_to_cpu(sph->entry_id) == log_id) { + found = wdc_get_dev_mng_log_entry(hdr_ptr->length, log_id, hdr_ptr, &sph); + if (found) { *cbs_data = (void *)&sph->data; - found = true; - break; + } 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); } - length += le32_to_cpu(sph->length); } - end: free(data); return found; @@ -1270,6 +1814,7 @@ static __u32 wdc_dump_dui_data_v2(int fd, __u32 dataLen, __u64 offset, __u8 *dum { int ret; struct nvme_admin_cmd admin_cmd; + __u64 offset_lo, offset_hi; memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd)); admin_cmd.opcode = WDC_NVME_CAP_DUI_OPCODE; @@ -1277,8 +1822,11 @@ static __u32 wdc_dump_dui_data_v2(int fd, __u32 dataLen, __u64 offset, __u8 *dum admin_cmd.addr = (__u64)(uintptr_t)dump_data; admin_cmd.data_len = dataLen; admin_cmd.cdw10 = ((dataLen >> 2) - 1); - admin_cmd.cdw12 = (__u32)(offset & 0x00000000FFFFFFFF); - admin_cmd.cdw13 = (__u32)(offset >> 32); + offset_lo = offset & 0x00000000FFFFFFFF; + offset_hi = ((offset & 0xFFFFFFFF00000000) >> 32); + admin_cmd.cdw12 = (__u32)offset_lo; + admin_cmd.cdw13 = (__u32)offset_hi; + if (last_xfer) admin_cmd.cdw14 = 0; else @@ -1609,6 +2157,7 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in struct wdc_dui_log_hdr_v3 *log_hdr_v3; __u32 cap_dui_length; __u64 cap_dui_length_v3; + __u64 cap_dui_length_v4; __u8 *dump_data = NULL; __u8 *buffer_addr; __s64 total_size = 0; @@ -1634,7 +2183,100 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in } /* Check the Log Header version */ - if (((log_hdr->hdr_version & 0xFF) == 0x02) || + if ((log_hdr->hdr_version & 0xFF) == 0x00 || + (log_hdr->hdr_version & 0xFF) == 0x01) { + __s32 log_size = 0; + __u32 curr_data_offset = 0; + + cap_dui_length = le32_to_cpu(log_hdr->log_size); + + if (verbose) { + fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log, data area = %d\n", data_area); + fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr->hdr_version); + } + + if (cap_dui_length == 0) { + fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log is empty\n"); + } else { + /* parse log header for all sections up to specified data area inclusively */ + if (data_area != WDC_NVME_DUI_MAX_DATA_AREA) { + for(j = 0; j < WDC_NVME_DUI_MAX_SECTION; j++) { + if (log_hdr->log_section[j].data_area_id <= data_area && + log_hdr->log_section[j].data_area_id != 0) { + log_size += log_hdr->log_section[j].section_size; + if (verbose) + fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%x\n", + __func__, log_hdr->log_section[j].data_area_id, (unsigned int)log_hdr->log_section[j].section_size, (unsigned int)log_size); + + } + else { + if (verbose) + fprintf(stderr, "%s: break, total size = 0x%x\n", __func__, (unsigned int)log_size); + break; + } + } + } else + log_size = cap_dui_length; + + total_size = log_size; + + dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size); + if (dump_data == NULL) { + fprintf(stderr, "%s: ERROR : dump data V1 malloc failed : status %s, size = 0x%x\n", + __func__, strerror(errno), (unsigned int)xfer_size); + ret = -1; + goto out; + } + memset(dump_data, 0, sizeof (__u8) * xfer_size); + + output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) { + fprintf(stderr, "%s: Failed to open output file %s: %s!\n", + __func__, file, strerror(errno)); + ret = output; + goto free_mem; + } + + /* write the telemetry and log headers into the dump_file */ + err = write(output, (void *)log_hdr, WDC_NVME_CAP_DUI_HEADER_SIZE); + if (err != WDC_NVME_CAP_DUI_HEADER_SIZE) { + fprintf(stderr, "%s: Failed to flush header data to file!\n", __func__); + goto free_mem; + } + + log_size -= WDC_NVME_CAP_DUI_HEADER_SIZE; + curr_data_offset = WDC_NVME_CAP_DUI_HEADER_SIZE; + i = 0; + buffer_addr = dump_data; + + for(; log_size > 0; log_size -= xfer_size) { + xfer_size = min(xfer_size, log_size); + + if (log_size <= xfer_size) + last_xfer = true; + + ret = wdc_dump_dui_data(fd, xfer_size, curr_data_offset, buffer_addr, last_xfer); + if (ret != 0) { + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%"PRIx64", offset = 0x%x, addr = %p\n", + __func__, i, (uint64_t)log_size, curr_data_offset, buffer_addr); + fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); + break; + } + + /* write the dump data into the file */ + err = write(output, (void *)buffer_addr, xfer_size); + if (err != xfer_size) { + fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%x\n", + __func__, i, err, xfer_size); + goto free_mem; + } + + curr_data_offset += xfer_size; + i++; + } + } + } + else if (((log_hdr->hdr_version & 0xFF) == 0x02) || ((log_hdr->hdr_version & 0xFF) == 0x03)) { /* Process Version 2 or 3 header */ __s64 log_size = 0; __u64 curr_data_offset = 0; @@ -1648,8 +2290,8 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in fprintf(stderr, "INFO : WDC : Capture V2 or V3 Device Unit Info log, data area = %d\n", data_area); fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr_v3->hdr_version); - if (log_hdr_v3->hdr_version >= 0x03) - fprintf(stderr, "INFO : WDC : DUI Product ID = %c\n", log_hdr_v3->product_id); + if ((log_hdr->hdr_version & 0xFF) == 0x03) + fprintf(stderr, "INFO : WDC : DUI Product ID = 0x%x/%c\n", log_hdr_v3->product_id, log_hdr_v3->product_id); } if (cap_dui_length_v3 == 0) { @@ -1662,12 +2304,12 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in log_hdr_v3->log_section[j].data_area_id != 0) { log_size += log_hdr_v3->log_section[j].section_size; if (verbose) - fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%lx\n", - __func__, log_hdr_v3->log_section[j].data_area_id, (unsigned int)log_hdr_v3->log_section[j].section_size, (long unsigned int)log_size); + fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%"PRIx64"\n", + __func__, log_hdr_v3->log_section[j].data_area_id, (unsigned int)log_hdr_v3->log_section[j].section_size, (uint64_t)log_size); } else { if (verbose) - fprintf(stderr, "%s: break, total size = 0x%lx\n", __func__, (long unsigned int)log_size); + fprintf(stderr, "%s: break, total size = 0x%"PRIx64"\n", __func__, (uint64_t)log_size); break; } } @@ -1684,8 +2326,8 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size_long); if (dump_data == NULL) { - fprintf(stderr, "%s: ERROR : dump data v3 malloc failed : status %s, size = 0x%lx\n", - __func__, strerror(errno), (long unsigned int)xfer_size_long); + fprintf(stderr, "%s: ERROR : dump data v3 malloc failed : status %s, size = 0x%"PRIx64"\n", + __func__, strerror(errno), (uint64_t)xfer_size_long); ret = -1; goto out; } @@ -1727,8 +2369,8 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in ret = wdc_dump_dui_data_v2(fd, (__u32)xfer_size_long, curr_data_offset, buffer_addr, last_xfer); if (ret != 0) { - fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%lx, offset = 0x%lx, addr = 0x%lx\n", - __func__, i, (long unsigned int)total_size, (long unsigned int)curr_data_offset, (long unsigned int)buffer_addr); + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%"PRIx64", offset = 0x%"PRIx64", addr = %p\n", + __func__, i, (uint64_t)total_size, (uint64_t)curr_data_offset, buffer_addr); fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); break; } @@ -1736,8 +2378,8 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in /* write the dump data into the file */ err = write(output, (void *)buffer_addr, xfer_size_long); if (err != xfer_size_long) { - fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%lx\n", - __func__, i, err, (long unsigned int)xfer_size_long); + fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%"PRIx64"\n", + __func__, i, err, (uint64_t)xfer_size_long); goto free_mem; } @@ -1745,50 +2387,65 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in i++; } } - } else { - __s32 log_size = 0; - __u32 curr_data_offset = 0; + } + else if ((log_hdr->hdr_version & 0xFF) == 0x04) { + __s64 log_size = 0; + __u64 curr_data_offset = 0; + struct wdc_dui_log_hdr_v4 *log_hdr_v4; + log_hdr_v4 = (struct wdc_dui_log_hdr_v4 *)log_hdr; + __s64 xfer_size_long = (__s64)xfer_size; + __s64 section_size_bytes = 0; - cap_dui_length = le32_to_cpu(log_hdr->log_size); + cap_dui_length_v4 = le64_to_cpu(log_hdr_v4->log_size_sectors) * WDC_NVME_SN730_SECTOR_SIZE; if (verbose) { - fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log, data area = %d\n", data_area); - fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr->hdr_version); + fprintf(stderr, "INFO : WDC : Capture V4 Device Unit Info log, data area = %d\n", data_area); + fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr_v4->hdr_version); + fprintf(stderr, "INFO : WDC : DUI Product ID = 0x%x/%c\n", log_hdr_v4->product_id, log_hdr_v4->product_id); + fprintf(stderr, "INFO : WDC : DUI log size sectors = 0x%x\n", log_hdr_v4->log_size_sectors); + fprintf(stderr, "INFO : WDC : DUI cap_dui_length = 0x%"PRIx64"\n", (uint64_t)cap_dui_length_v4); } - if (cap_dui_length == 0) { - fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log is empty\n"); + if (cap_dui_length_v4 == 0) { + fprintf(stderr, "INFO : WDC : Capture V4 Device Unit Info log is empty\n"); } else { /* parse log header for all sections up to specified data area inclusively */ if (data_area != WDC_NVME_DUI_MAX_DATA_AREA) { for(j = 0; j < WDC_NVME_DUI_MAX_SECTION; j++) { - if (log_hdr->log_section[j].data_area_id <= data_area && - log_hdr->log_section[j].data_area_id != 0) { - log_size += log_hdr->log_section[j].section_size; + if (log_hdr_v4->log_section[j].data_area_id <= data_area && + log_hdr_v4->log_section[j].data_area_id != 0) { + section_size_bytes = ((__s64)log_hdr_v4->log_section[j].section_size_sectors * WDC_NVME_SN730_SECTOR_SIZE); + log_size += section_size_bytes; if (verbose) - fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%x\n", - __func__, log_hdr->log_section[j].data_area_id, (unsigned int)log_hdr->log_section[j].section_size, (unsigned int)log_size); - + fprintf(stderr, "%s: Data area ID %d : section size 0x%x sectors, section size 0x%"PRIx64" bytes, total size = 0x%"PRIx64"\n", + __func__, log_hdr_v4->log_section[j].data_area_id, log_hdr_v4->log_section[j].section_size_sectors, (uint64_t)section_size_bytes, + (uint64_t)log_size); } else { if (verbose) - fprintf(stderr, "%s: break, total size = 0x%x\n", __func__, (unsigned int)log_size); + fprintf(stderr, "%s: break, total size = 0x%"PRIx64"\n", __func__, (uint64_t)log_size); break; } } } else - log_size = cap_dui_length; + log_size = cap_dui_length_v4; total_size = log_size; - dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size); + if (offset >= total_size) { + fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64" exceeds total size 0x%"PRIx64", no data retrieved\n", + __func__, (uint64_t)offset, (uint64_t)total_size); + goto out; + } + + dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size_long); if (dump_data == NULL) { - fprintf(stderr, "%s: ERROR : dump data V1 malloc failed : status %s, size = 0x%x\n", - __func__, strerror(errno), (unsigned int)xfer_size); + fprintf(stderr, "%s: ERROR : dump data V4 malloc failed : status %s, size = 0x%x\n", + __func__, strerror(errno), (unsigned int)xfer_size_long); ret = -1; goto out; } - memset(dump_data, 0, sizeof (__u8) * xfer_size); + memset(dump_data, 0, sizeof (__u8) * xfer_size_long); output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (output < 0) { @@ -1798,49 +2455,62 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in goto free_mem; } - /* write the telemetry and log headers into the dump_file */ - err = write(output, (void *)log_hdr, WDC_NVME_CAP_DUI_HEADER_SIZE); - if (err != WDC_NVME_CAP_DUI_HEADER_SIZE) { - fprintf(stderr, "%s: Failed to flush header data to file!\n", __func__); - goto free_mem; + curr_data_offset = 0; + + if (file_size != 0) { + /* Write the DUI data based on the passed in file size */ + if ((offset + file_size) > total_size) + log_size = min((total_size - offset), file_size); + else + log_size = min(total_size, file_size); + + if (verbose) + fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64", file size 0x%"PRIx64", total size 0x%"PRIx64", log size 0x%"PRIx64"\n", + __func__, (uint64_t)offset, (uint64_t)file_size, (uint64_t)total_size, (uint64_t)log_size); + + curr_data_offset = offset; + } - log_size -= WDC_NVME_CAP_DUI_HEADER_SIZE; - curr_data_offset = WDC_NVME_CAP_DUI_HEADER_SIZE; i = 0; buffer_addr = dump_data; - for(; log_size > 0; log_size -= xfer_size) { - xfer_size = min(xfer_size, log_size); + for(; log_size > 0; log_size -= xfer_size_long) { + xfer_size_long = min(xfer_size_long, log_size); - if (log_size <= xfer_size) + if (log_size <= xfer_size_long) last_xfer = true; - ret = wdc_dump_dui_data(fd, xfer_size, curr_data_offset, buffer_addr, last_xfer); + ret = wdc_dump_dui_data_v2(fd, (__u32)xfer_size_long, curr_data_offset, buffer_addr, last_xfer); if (ret != 0) { - fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%lx, offset = 0x%x, addr = %p\n", - __func__, i, (long unsigned int)log_size, curr_data_offset, buffer_addr); + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%"PRIx64", offset = 0x%"PRIx64", addr = %p\n", + __func__, i, (uint64_t)log_size, (uint64_t)curr_data_offset, buffer_addr); fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); break; } /* write the dump data into the file */ - err = write(output, (void *)buffer_addr, xfer_size); - if (err != xfer_size) { - fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%x\n", - __func__, i, err, xfer_size); + err = write(output, (void *)buffer_addr, xfer_size_long); + if (err != xfer_size_long) { + fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size_long = 0x%"PRIx64"\n", + __func__, i, err, (uint64_t)xfer_size_long); goto free_mem; } - curr_data_offset += xfer_size; + curr_data_offset += xfer_size_long; i++; } } } + else { + fprintf(stderr, "INFO : WDC : Unsupported header version = 0x%x\n", log_hdr->hdr_version); + goto out; + } + fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); if (verbose) - fprintf(stderr, "INFO : WDC : Capture Device Unit Info log, length = 0x%lx\n", (long unsigned int)total_size); + fprintf(stderr, "INFO : WDC : Capture Device Unit Info log, length = 0x%"PRIx64"\n", (uint64_t)total_size); free_mem: close(output); @@ -2137,7 +2807,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command char *desc = "Internal Firmware Log."; char *file = "Output file pathname."; char *size = "Data retrieval transfer size."; - char *data_area = "Data area to retrieve up to. Currently only supported on the SN340, SN640, and SN840 devices."; + char *data_area = "Data area to retrieve up to. Currently only supported on the SN340, SN640, SN730, and SN840 devices."; char *file_size = "Output file size. Currently only supported on the SN340 device."; char *offset = "Output file data offset. Currently only supported on the SN340 device."; char *type = "Telemetry type - NONE, HOST, or CONTROLLER. Currently only supported on the SN640 and SN840 devices."; @@ -2164,7 +2834,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command struct config cfg = { .file = NULL, .xfer_size = 0x10000, - .data_area = 3, + .data_area = 0, .file_size = 0, .offset = 0, .type = NULL, @@ -2224,13 +2894,18 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command snprintf(f + strlen(f), PATH_MAX, "%s", ".bin"); fprintf(stderr, "%s: filename = %s\n", __func__, f); - if (cfg.data_area > 5 || cfg.data_area == 0) { - fprintf(stderr, "ERROR : WDC: Data area must be 1-5\n"); - return -1; + if (cfg.data_area) { + if (cfg.data_area > 5 || cfg.data_area < 1) { + fprintf(stderr, "ERROR : WDC: Data area must be 1-5\n"); + return -1; + } } capabilities = wdc_get_drive_capabilities(fd); if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) { + if (cfg.data_area == 0) + cfg.data_area = 3; /* Set the default DA to 3 if not specified */ + if ((cfg.type == NULL) || (!strcmp(cfg.type, "NONE")) || (!strcmp(cfg.type, "none"))) { @@ -2251,7 +2926,11 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command return wdc_do_cap_diag(fd, f, xfer_size, telemetry_type, telemetry_data_area); } - if ((capabilities & WDC_DRIVE_CAP_SN340_DUI) == WDC_DRIVE_CAP_SN340_DUI) { + if ((capabilities & WDC_DRIVE_CAP_DUI) == WDC_DRIVE_CAP_DUI) { + if (cfg.data_area == 0) { + cfg.data_area = 1; + } + /* FW requirement - xfer size must be 256k for data area 4 */ if (cfg.data_area >= 4) xfer_size = 0x40000; @@ -2924,119 +3603,112 @@ static void wdc_print_bd_ca_log_normal(void *data) { struct wdc_bd_ca_log_format *bd_data = (struct wdc_bd_ca_log_format *)data; __u64 *raw; - __u16 *word_raw; + __u16 *word_raw1, *word_raw2, *word_raw3; __u32 *dword_raw; __u8 *byte_raw; if (bd_data->field_id == 0x00) { raw = (__u64*)bd_data->raw_value; - printf(" CA Log Page values :- \n"); - printf(" Program fail counts %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - printf(" %% Remaining of allowable program fails %3"PRIu8"\n", - bd_data->normalized_value); + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", + devicename, WDC_DE_GLOBAL_NSID); + printf("key normalized raw\n"); + printf("program_fail_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x01) { raw = (__u64*)bd_data->raw_value; - printf(" Erase fail count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - printf(" %% Remaining of allowable erase fails %3"PRIu8"\n", - bd_data->normalized_value); + printf("erase_fail_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x02) { - word_raw = (__u16*)bd_data->raw_value; - printf(" Min erase cycles %10"PRIu16"\n", - le16_to_cpu(*word_raw)); - word_raw = (__u16*)&bd_data->raw_value[2]; - printf(" Max erase cycles %10"PRIu16"\n", - le16_to_cpu(*word_raw)); - word_raw = (__u16*)&bd_data->raw_value[4]; - printf(" Ave erase cycles %10"PRIu16"\n", - le16_to_cpu(*word_raw)); - printf(" Wear Leveling Normalized %3"PRIu8"\n", - bd_data->normalized_value); - + word_raw1 = (__u16*)bd_data->raw_value; + word_raw2 = (__u16*)&bd_data->raw_value[2]; + word_raw3 = (__u16*)&bd_data->raw_value[4]; + printf("wear_leveling : %3"PRIu8"%% min: %"PRIu16", max: %"PRIu16", avg: %"PRIu16"\n", + bd_data->normalized_value, + le16_to_cpu(*word_raw1), + le16_to_cpu(*word_raw2), + le16_to_cpu(*word_raw3)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x03) { raw = (__u64*)bd_data->raw_value; - printf(" End to end error detection count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("end_to_end_error_detection_count: %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x04) { raw = (__u64*)bd_data->raw_value; - printf(" Crc error count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("crc_error_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x05) { raw = (__u64*)bd_data->raw_value; - printf(" Timed workload media error %20.3f\n", - safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); + printf("timed_workload_media_wear : %3"PRIu8"%% %-.3f%%\n", + bd_data->normalized_value, + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x06) { raw = (__u64*)bd_data->raw_value; - printf(" Timed workload host reads %% %3"PRIu64"\n", - le64_to_cpu(*raw & 0x00000000000000FF)); + printf("timed_workload_host_reads : %3"PRIu8"%% %"PRIu64"%%\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x07) { raw = (__u64*)bd_data->raw_value; - printf(" Timed workload timer %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("timed_workload_timer : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x08) { byte_raw = (__u8*)bd_data->raw_value; - printf(" Throttle status %% %10"PRIu16"\n", - *byte_raw); dword_raw = (__u32*)&bd_data->raw_value[1]; - printf(" Throttling event counter %10"PRIu16"\n", - le32_to_cpu(*dword_raw)); + printf("thermal_throttle_status : %3"PRIu8"%% %"PRIu16"%%, cnt: %"PRIu16"\n", + bd_data->normalized_value, *byte_raw, le32_to_cpu(*dword_raw)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x09) { raw = (__u64*)bd_data->raw_value; - printf(" Retry buffer overflow count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("retry_buffer_overflow_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x0A) { raw = (__u64*)bd_data->raw_value; - printf(" Pll lock loss count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("pll_lock_loss_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x0B) { raw = (__u64*)bd_data->raw_value; - printf(" Nand bytes written (32mb) %20.0f\n", - safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + printf("nand_bytes_written : %3"PRIu8"%% sectors: %.f\n", + bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); raw = (__u64*)bd_data->raw_value; } else { goto invalid_id; @@ -3044,8 +3716,8 @@ static void wdc_print_bd_ca_log_normal(void *data) bd_data++; if (bd_data->field_id == 0x0C) { raw = (__u64*)bd_data->raw_value; - printf(" Host bytes written (32mb) %20.0f\n", - safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + printf("host_bytes_written : %3"PRIu8"%% sectors: %.f\n", + bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); raw = (__u64*)bd_data->raw_value; } else { goto invalid_id; @@ -3073,9 +3745,9 @@ static void wdc_print_bd_ca_log_json(void *data) root = json_create_object(); if (bd_data->field_id == 0x00) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Program fail counts", + json_object_add_value_int(root, "program_fail_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - json_object_add_value_int(root, "% Remaining of allowable program fails", + json_object_add_value_int(root, "normalized", bd_data->normalized_value); } else { goto invalid_id; @@ -3083,9 +3755,9 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x01) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Erase fail count", + json_object_add_value_int(root, "erase_fail_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - json_object_add_value_int(root, "% Remaining of allowable erase fails", + json_object_add_value_int(root, "normalized", bd_data->normalized_value); } else { goto invalid_id; @@ -3093,19 +3765,19 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x02) { word_raw = (__u16*)bd_data->raw_value; - json_object_add_value_int(root, "Min erase cycles", le16_to_cpu(*word_raw)); + json_object_add_value_int(root, "min", le16_to_cpu(*word_raw)); word_raw = (__u16*)&bd_data->raw_value[2]; - json_object_add_value_int(root, "Max erase cycles", le16_to_cpu(*word_raw)); + json_object_add_value_int(root, "max", le16_to_cpu(*word_raw)); word_raw = (__u16*)&bd_data->raw_value[4]; - json_object_add_value_int(root, "Ave erase cycles", le16_to_cpu(*word_raw)); - json_object_add_value_int(root, "Wear Leveling Normalized", bd_data->normalized_value); + json_object_add_value_int(root, "avg", le16_to_cpu(*word_raw)); + json_object_add_value_int(root, "wear_leveling-normalized", bd_data->normalized_value); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x03) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "End to end error detection count", + json_object_add_value_int(root, "end_to_end_error_detection_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3113,7 +3785,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x04) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Crc error count", + json_object_add_value_int(root, "crc_error_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3121,7 +3793,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x05) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_float(root, "Timed workload media error", + json_object_add_value_float(root, "timed_workload_media_wear", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); } else { goto invalid_id; @@ -3129,7 +3801,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x06) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Timed workload host reads %", + json_object_add_value_int(root, "timed_workload_host_reads", le64_to_cpu(*raw & 0x00000000000000FF)); } else { goto invalid_id; @@ -3137,7 +3809,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x07) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Timed workload timer", + json_object_add_value_int(root, "timed_workload_timer", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3145,16 +3817,16 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x08) { byte_raw = (__u8*)bd_data->raw_value; - json_object_add_value_int(root, "Throttle status %", *byte_raw); + json_object_add_value_int(root, "thermal_throttle_status", *byte_raw); dword_raw = (__u32*)&bd_data->raw_value[1]; - json_object_add_value_int(root, "Throttling event counter", le32_to_cpu(*dword_raw)); + json_object_add_value_int(root, "cnt", le32_to_cpu(*dword_raw)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x09) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Retry buffer overflow count", + json_object_add_value_int(root, "retry_buffer_overflow_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3162,7 +3834,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x0A) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Pll lock loss count", + json_object_add_value_int(root, "pll_lock_loss_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3170,7 +3842,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x0B) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_float(root, "Nand bytes written (32mb)", + json_object_add_value_float(root, "nand_bytes_written", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); } else { goto invalid_id; @@ -3178,7 +3850,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x0C) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_float(root, "Host bytes written (32mb)", + json_object_add_value_float(root, "host_bytes_written", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); raw = (__u64*)bd_data->raw_value; } else { @@ -3334,61 +4006,162 @@ static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin) } -static void wdc_print_fw_act_history_log_normal(struct wdc_fw_act_history_log_entry *fw_act_history_entry, - int num_entries) +static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u32 cust_id) { int i; char previous_fw[9]; char new_fw[9]; char commit_action_bin[8]; - memset((void *)previous_fw, 0, 9); - memset((void *)new_fw, 0, 9); - memset((void *)commit_action_bin, 0, 8); + char time_str[11]; + __u16 oldestEntryIdx = 0, entryIdx = 0; char *null_fw = "--------"; - - - printf(" Firmware Activate History Log \n"); - printf(" Power on Hour Power Cycle Previous New \n"); - printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); - printf(" ----- -------------- ------------ ---------- ---------- ----- ------ -------\n"); - - for (i = 0; i < num_entries; i++) { - memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); - if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) - memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); - else - memcpy(new_fw, null_fw, 8); - - printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->entry_num)); - printf(" "); - printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), - (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), - (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); - printf(" "); - printf("%8"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->power_cycle_count)); - printf(" "); - printf("%s", (char *)previous_fw); - printf(" "); - printf("%s", (char *)new_fw); - printf(" "); - printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->slot_number); - printf(" "); - wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); - printf(" %s", (char *)commit_action_bin); - printf(" "); - if (le16_to_cpu(fw_act_history_entry->result) == 0) - printf("pass"); + memset((void *)time_str, 0, 11); + + 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) { + printf(" Power on Hour Power Cycle Previous New \n"); + printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); + printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); + } else { + printf(" Power Cycle Previous New \n"); + printf(" Entry Timestamp Count Firmware Firmware Slot Action Result \n"); + printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); + } + struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > + le16_to_cpu(fw_act_history_entry->entry[i+1].fw_act_hist_entries)) { + oldestEntryIdx = i+1; + break; + } + } + } + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; else - printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->result)); + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memset((void *)previous_fw, 0, 9); + memset((void *)new_fw, 0, 9); + memset((void *)commit_action_bin, 0, 8); + + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + printf("%5"PRIu16"", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); + if (cust_id == WDC_CUSTOMER_ID_0x1005) { + printf(" "); + memset((void *)time_str, 0, 9); + sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)/3600), + (int)((le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%3600)/60)), + (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%60))); + + printf("%s", time_str); + printf(" "); + } else { + printf(" "); + uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); + printf("%16"PRIu64"", timestamp); + printf(" "); + } - printf("\n"); + printf("%16"PRIu64"", (uint64_t)le64_to_cpu(fw_act_history_entry->entry[entryIdx].power_cycle_count)); + printf(" "); + printf("%s", (char *)previous_fw); + printf(" "); + printf("%s", (char *)new_fw); + printf(" "); + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->entry[entryIdx].slot_number); + printf(" "); + wdc_get_commit_action_bin(fw_act_history_entry->entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + printf(" %s", (char *)commit_action_bin); + printf(" "); + if (le16_to_cpu(fw_act_history_entry->entry[entryIdx].result) == 0) + printf("pass"); + else + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)); + printf("\n"); + + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + } + } + else + { + printf(" Firmware Activate History Log \n"); + printf(" Power on Hour Power Cycle Previous New \n"); + printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); + printf(" ----- -------------- -------------------- ---------- ---------- ----- ------ -------\n"); + + struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le32_to_cpu(fw_act_history_entry[i].entry_num) > le32_to_cpu(fw_act_history_entry[i+1].entry_num)) { + oldestEntryIdx = i+1; + break; + } + } + } - fw_act_history_entry++; + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + else + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memset((void *)previous_fw, 0, 9); + memset((void *)new_fw, 0, 9); + memset((void *)commit_action_bin, 0, 8); + + memcpy(previous_fw, (char *)&(fw_act_history_entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry[entryIdx].new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry[entryIdx].new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry[entryIdx].entry_num)); + printf(" "); + printf("%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%60)); + printf(" "); + printf("%16"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry[entryIdx].power_cycle_count)); + printf(" "); + printf("%s", (char *)previous_fw); + printf(" "); + printf("%s", (char *)new_fw); + printf(" "); + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry[entryIdx].slot_number); + printf(" "); + wdc_get_commit_action_bin(fw_act_history_entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + printf(" %s", (char *)commit_action_bin); + printf(" "); + if (le16_to_cpu(fw_act_history_entry[entryIdx].result) == 0) + printf("pass"); + else + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry[entryIdx].result)); + + printf("\n"); + + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + } } } -static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entry *fw_act_history_entry, - int num_entries) +static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32 cust_id) { struct json_object *root; int i; @@ -3396,59 +4169,569 @@ static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entr char new_fw[9]; char commit_action_bin[8]; char fail_str[32]; - char time_str[9]; + char time_str[11]; memset((void *)previous_fw, 0, 9); memset((void *)new_fw, 0, 9); memset((void *)commit_action_bin, 0, 8); - memset((void *)time_str, 0, 9); + memset((void *)time_str, 0, 11); memset((void *)fail_str, 0, 11); char *null_fw = "--------"; + __u16 oldestEntryIdx = 0, entryIdx = 0; root = json_create_object(); - for (i = 0; i < num_entries; i++) { - memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); - if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) - memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); + if(data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { + struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > + le16_to_cpu(fw_act_history_entry->entry[i+1].fw_act_hist_entries)) { + oldestEntryIdx = i+1; + break; + } + } + } + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; else - memcpy(new_fw, null_fw, 8); - - json_object_add_value_int(root, "Entry", - le32_to_cpu(fw_act_history_entry->entry_num)); - - sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), - (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), - (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); - json_object_add_value_string(root, "Power on Hour", time_str); - - json_object_add_value_int(root, "Power Cycle Count", - le32_to_cpu(fw_act_history_entry->power_cycle_count)); - json_object_add_value_string(root, "Previous Firmware", - previous_fw); - json_object_add_value_string(root, "New Firmware", - new_fw); - json_object_add_value_int(root, "Slot", - fw_act_history_entry->slot_number); - - wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); - json_object_add_value_string(root, "Action", commit_action_bin); - - if (le16_to_cpu(fw_act_history_entry->result) == 0) - json_object_add_value_string(root, "Result", "pass"); - else { - sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->result))); - json_object_add_value_string(root, "Result", fail_str); + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + json_object_add_value_int(root, "Entry", + le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); + + if (cust_id == WDC_CUSTOMER_ID_0x1005) { + sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)/3600), + (int)((le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%3600)/60)), + (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%60))); + + json_object_add_value_string(root, "Power on Hour", time_str); + + } else { + uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); + json_object_add_value_int(root, "Timestamp", timestamp); + } + + json_object_add_value_int(root, "Power Cycle Count", + le64_to_cpu(fw_act_history_entry->entry[entryIdx].power_cycle_count)); + json_object_add_value_string(root, "Previous Firmware", + previous_fw); + json_object_add_value_string(root, "New Firmware", + new_fw); + json_object_add_value_int(root, "Slot", + fw_act_history_entry->entry[entryIdx].slot_number); + + wdc_get_commit_action_bin(fw_act_history_entry->entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + json_object_add_value_string(root, "Action", commit_action_bin); + + if (le16_to_cpu(fw_act_history_entry->entry[entryIdx].result) == 0) + json_object_add_value_string(root, "Result", "pass"); + else { + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->entry[entryIdx].result))); + json_object_add_value_string(root, "Result", fail_str); + } + + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + } + } + else { + struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le32_to_cpu(fw_act_history_entry[i].entry_num) > le32_to_cpu(fw_act_history_entry[i+1].entry_num)) { + oldestEntryIdx = i+1; + break; + } + } + } + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + else + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry[entryIdx].new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry[entryIdx].new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + json_object_add_value_int(root, "Entry", + le32_to_cpu(fw_act_history_entry[entryIdx].entry_num)); + + sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%60)); + json_object_add_value_string(root, "Power on Hour", time_str); + + json_object_add_value_int(root, "Power Cycle Count", + le32_to_cpu(fw_act_history_entry[entryIdx].power_cycle_count)); + json_object_add_value_string(root, "Previous Firmware", + previous_fw); + json_object_add_value_string(root, "New Firmware", + new_fw); + json_object_add_value_int(root, "Slot", + fw_act_history_entry[entryIdx].slot_number); + + wdc_get_commit_action_bin(fw_act_history_entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + json_object_add_value_string(root, "Action", commit_action_bin); + + if (le16_to_cpu(fw_act_history_entry[entryIdx].result) == 0) + json_object_add_value_string(root, "Result", "pass"); + else { + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry[entryIdx].result))); + json_object_add_value_string(root, "Result", fail_str); + } + + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; } + } + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); +} - fw_act_history_entry++; +static void wdc_print_smart_cloud_attr_C0_normal(void *data) +{ + __u8 *log_data = (__u8*)data; + uint16_t smart_log_ver = 0; + + printf(" SMART Cloud Attributes :- \n"); + + printf(" Physical media units written %.0Lf\n", + int128_to_double(&log_data[SCAO_PMUW])); + printf(" Physical media units Read %.0Lf\n", + int128_to_double(&log_data[SCAO_PMUR])); + printf(" Bad user nand blocks - Raw %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); + printf(" Bad user nand blocks - Normalized %d\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); + printf(" Bad system nand blocks - Raw %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); + printf(" Bad system nand blocks - Normalized %d\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); + printf(" XOR recovery count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); + printf(" Uncorrectable read error count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); + printf(" Soft ecc error count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); + printf(" End to end corrected errors %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); + printf(" End to end detected errors %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); + printf(" System data percent used %d\n", + (__u8)log_data[SCAO_SDPU]); + printf(" Refresh counts %"PRIu64"\n", + (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); + printf(" Max User data erase counts %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); + printf(" Min User data erase counts %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); + printf(" Number of Thermal throttling events %d\n", + (__u8)log_data[SCAO_NTTE]); + printf(" Current throttling status 0x%x\n", + (__u8)log_data[SCAO_CTS]); + printf(" PCIe correctable error count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); + printf(" Incomplete shutdowns %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); + printf(" Percent free blocks %d\n", + (__u8)log_data[SCAO_PFB]); + printf(" Capacitor health %"PRIu16"\n", + (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(" 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])); + printf(" PLP start count %.0Lf\n", + int128_to_double(&log_data[SCAO_PSC])); + printf(" Endurance estimate %.0Lf\n", + int128_to_double(&log_data[SCAO_EEST])); + smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); + printf(" Log page version %"PRIu16"\n",smart_log_ver); + printf(" Log page GUID 0x"); + printf("%lX%lX\n",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); + if(smart_log_ver > 2) { + printf(" Errata Version Field %d\n", + (__u8)log_data[SCAO_EVF]); + printf(" Point Version Field %"PRIu16"\n", + (uint16_t)log_data[SCAO_PVF]); + printf(" Minor Version Field %"PRIu16"\n", + (uint16_t)log_data[SCAO_MIVF]); + printf(" Major Version Field %d\n", + (__u8)log_data[SCAO_MAVF]); + printf(" NVMe Errata Version %d\n", + (__u8)log_data[SCAO_NEV]); + printf(" PCIe Link Retraining Count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); } + printf("\n"); +} +static void wdc_print_smart_cloud_attr_C0_json(void *data) +{ + __u8 *log_data = (__u8*)data; + struct json_object *root; + uint16_t smart_log_ver = 0; + + root = json_create_object(); + json_object_add_value_float(root, "Physical media units written", + int128_to_double(&log_data[SCAO_PMUW])); + json_object_add_value_int(root, "Physical media units Read", + int128_to_double(&log_data[SCAO_PMUR])); + json_object_add_value_uint(root, "Bad user nand blocks - Raw", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); + json_object_add_value_uint(root, "Bad user nand blocks - Normalized", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); + json_object_add_value_uint(root, "Bad system nand blocks - Raw", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); + json_object_add_value_uint(root, "Bad system nand blocks - Normalized", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); + json_object_add_value_uint(root, "XOR recovery count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); + json_object_add_value_uint(root, "Uncorrectable read error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); + json_object_add_value_uint(root, "Soft ecc error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); + json_object_add_value_uint(root, "End to end corrected errors", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); + json_object_add_value_uint(root, "End to end detected errors", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); + json_object_add_value_uint(root, "System data percent used", + (__u8)log_data[SCAO_SDPU]); + json_object_add_value_uint(root, "Refresh counts", + (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); + json_object_add_value_uint(root, "Max User data erase counts", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); + json_object_add_value_uint(root, "Min User data erase counts", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); + json_object_add_value_uint(root, "Number of Thermal throttling events", + (__u8)log_data[SCAO_NTTE]); + json_object_add_value_uint(root, "Current throttling status", + (__u8)log_data[SCAO_CTS]); + json_object_add_value_uint(root, "PCIe correctable error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); + json_object_add_value_uint(root, "Incomplete shutdowns", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); + json_object_add_value_uint(root, "Percent free blocks", + (__u8)log_data[SCAO_PFB]); + json_object_add_value_uint(root, "Capacitor health", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); + json_object_add_value_uint(root, "Unaligned I/O", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); + json_object_add_value_uint(root, "Security Version Number", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); + json_object_add_value_uint(root, "NUSE - Namespace utilization", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); + json_object_add_value_uint(root, "PLP start count", + int128_to_double(&log_data[SCAO_PSC])); + json_object_add_value_uint(root, "Endurance estimate", + int128_to_double(&log_data[SCAO_EEST])); + smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); + json_object_add_value_uint(root, "Log page version", smart_log_ver); + char guid[40]; + memset((void*)guid, 0, 40); + sprintf((char*)guid, "0x%lX%lX",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); + printf("GUID string:%s", guid); + json_object_add_value_string(root, "Log page GUID", guid); + if(smart_log_ver > 2){ + json_object_add_value_uint(root, "Errata Version Field", + (__u8)log_data[SCAO_EVF]); + json_object_add_value_uint(root, "Point Version Field", + (uint16_t)log_data[SCAO_PVF]); + json_object_add_value_uint(root, "Minor Version Field", + (uint16_t)log_data[SCAO_MIVF]); + json_object_add_value_uint(root, "Major Version Field", + (__u8)log_data[SCAO_MAVF]); + json_object_add_value_uint(root, "NVMe Errata Version", + (__u8)log_data[SCAO_NEV]); + json_object_add_value_uint(root, "PCIe Link Retraining Count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); + } json_print_object(root, NULL); printf("\n"); + json_free_object(root); +} + +static void wdc_print_eol_c0_normal(void *data) +{ + __u8 *log_data = (__u8*)data; + + printf(" End of Life Log Page 0xC0 :- \n"); + + printf(" Realloc Block Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_RBC])); + printf(" ECC Rate %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_ECCR])); + printf(" Write Amp %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_WRA])); + printf(" Percent Life Remaining %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_PLR])); + printf(" Program Fail Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_PFC])); + printf(" Erase Fail Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_EFC])); + printf(" Raw Read Error Rate %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_RRER])); + +} + +static void wdc_print_eol_c0_json(void *data) +{ + __u8 *log_data = (__u8*)data; + struct json_object *root; + + root = json_create_object(); + + json_object_add_value_uint(root, "Realloc Block Count", + (uint32_t)le32_to_cpu(log_data[EOL_RBC])); + json_object_add_value_uint(root, "ECC Rate", + (uint32_t)le32_to_cpu(log_data[EOL_ECCR])); + json_object_add_value_uint(root, "Write Amp", + (uint32_t)le32_to_cpu(log_data[EOL_WRA])); + json_object_add_value_uint(root, "Percent Life Remaining", + (uint32_t)le32_to_cpu(log_data[EOL_PLR])); + json_object_add_value_uint(root, "Program Fail Count", + (uint32_t)le32_to_cpu(log_data[EOL_PFC])); + json_object_add_value_uint(root, "Erase Fail Count", + (uint32_t)le32_to_cpu(log_data[EOL_EFC])); + json_object_add_value_uint(root, "Raw Read Error Rate", + (uint32_t)le32_to_cpu(log_data[EOL_RRER])); + + json_print_object(root, NULL); + printf("\n"); json_free_object(root); } +static int wdc_print_c0_cloud_attr_log(void *data, int fmt) +{ + if (!data) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read 0xC0 log\n"); + return -1; + } + switch (fmt) { + case NORMAL: + wdc_print_smart_cloud_attr_C0_normal(data); + break; + case JSON: + wdc_print_smart_cloud_attr_C0_json(data); + break; + } + return 0; +} + +static int wdc_print_c0_eol_log(void *data, int fmt) +{ + if (!data) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read 0xC0 log\n"); + return -1; + } + switch (fmt) { + case NORMAL: + wdc_print_eol_c0_normal(data); + break; + case JSON: + wdc_print_eol_c0_json(data); + break; + } + return 0; +} + +static int wdc_get_c0_log_page(int fd, char *format, int uuid_index) +{ + int ret = 0; + int fmt = -1; + int i = 0; + __u8 *data; + __u32 *cust_id; + uint32_t device_id, read_vendor_id; + + if (!wdc_check_device(fd)) + return -1; + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + return fmt; + } + + ret = wdc_get_pci_ids(&device_id, &read_vendor_id); + + switch (device_id) { + + case WDC_NVME_SN640_DEV_ID: + case WDC_NVME_SN640_DEV_ID_1: + case WDC_NVME_SN640_DEV_ID_2: + case WDC_NVME_SN640_DEV_ID_3: + case WDC_NVME_SN840_DEV_ID: + case WDC_NVME_SN840_DEV_ID_1: + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + return -1; + } + + cust_id = (__u32*)data; + + if ((*cust_id == WDC_CUSTOMER_ID_0x1004) || (*cust_id == WDC_CUSTOMER_ID_0x1008) || + (*cust_id == WDC_CUSTOMER_ID_0x1005) || (*cust_id == WDC_CUSTOMER_ID_0x1304)) + { + if (uuid_index == 0) + { + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + NVME_NO_LOG_LSP, 0, 0, false, uuid_index, WDC_NVME_SMART_CLOUD_ATTR_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + + /* Verify GUID matches */ + for (i=0; i<16; i++) { + if (scao_guid[i] != data[SCAO_LPG + i]) { + fprintf(stderr, "ERROR : WDC : Unknown GUID in C0 Log Page data\n"); + int j; + fprintf(stderr, "ERROR : WDC : Expected GUID: 0x"); + for (j = 0; j<16; j++) { + fprintf(stderr, "%x", scao_guid[j]); + } + fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x"); + for (j = 0; j<16; j++) { + fprintf(stderr, "%x", data[SCAO_LPG + j]); + } + fprintf(stderr, "\n"); + + ret = -1; + break; + } + } + + if (ret == 0) { + + /* parse the data */ + wdc_print_c0_cloud_attr_log(data, fmt); + } + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + } else if (uuid_index == 1) { + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_EOL_STATUS_LOG_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + NVME_NO_LOG_LSP, 0, 0, false, uuid_index, WDC_NVME_EOL_STATUS_LOG_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + wdc_print_c0_eol_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + } else { + fprintf(stderr, "ERROR : WDC : Unknown uuid index\n"); + ret = -1; + } + } + else { + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_EOL_STATUS_LOG_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + false, NVME_NO_LOG_LSP, WDC_NVME_EOL_STATUS_LOG_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + wdc_print_c0_eol_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + } + break; + + case WDC_NVME_ZN350_DEV_ID: + case WDC_NVME_ZN350_DEV_ID_1: + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE, + false, NVME_NO_LOG_LSP, WDC_NVME_SMART_CLOUD_ATTR_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + wdc_print_c0_cloud_attr_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + break; + + default: + fprintf(stderr, "ERROR : WDC : Unknown device id - 0x%x\n", device_id); + + ret = -1; + break; + + } + + return ret; +} + static int wdc_print_fb_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt) { if (!perf) { @@ -3500,21 +4783,19 @@ 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(struct wdc_fw_act_history_log_entry *fw_act_history_entries, - int num_entries, - int fmt) +static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt, __u32 cust_id) { - if (!fw_act_history_entries) { + if (!data) { fprintf(stderr, "ERROR : WDC : Invalid buffer to read fw activate history entries\n"); return -1; } switch (fmt) { case NORMAL: - wdc_print_fw_act_history_log_normal(fw_act_history_entries, num_entries); + wdc_print_fw_act_history_log_normal(data, num_entries, cust_id); break; case JSON: - wdc_print_fw_act_history_log_json(fw_act_history_entries, num_entries); + wdc_print_fw_act_history_log_json(data, num_entries, cust_id); break; } return 0; @@ -3566,7 +4847,7 @@ static int wdc_get_ca_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, - false, WDC_FB_CA_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_FB_CA_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3580,7 +4861,7 @@ static int wdc_get_ca_log_page(int fd, char *format) } } else { - fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id); + fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = 0x%x\n", *cust_id); return -1; } break; @@ -3588,7 +4869,7 @@ static int wdc_get_ca_log_page(int fd, char *format) case WDC_NVME_SN640_DEV_ID: case WDC_NVME_SN640_DEV_ID_1: case WDC_NVME_SN640_DEV_ID_2: - case WDC_NVME_SN640_DEV_ID_3: + case WDC_NVME_SN640_DEV_ID_3: case WDC_NVME_SN840_DEV_ID: case WDC_NVME_SN840_DEV_ID_1: @@ -3602,7 +4883,7 @@ static int wdc_get_ca_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, - false, WDC_FB_CA_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_FB_CA_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3614,7 +4895,8 @@ static int wdc_get_ca_log_page(int fd, char *format) fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n"); ret = -1; } - } else if ((*cust_id == WDC_CUSTOMER_ID_GN) || (*cust_id == WDC_CUSTOMER_ID_GD)) { + } else if ((*cust_id == WDC_CUSTOMER_ID_GN) || (*cust_id == WDC_CUSTOMER_ID_GD) || + (*cust_id == WDC_CUSTOMER_ID_BD)) { if ((data = (__u8*) malloc(sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN)) == NULL) { fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); @@ -3623,7 +4905,7 @@ static int wdc_get_ca_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, - false, WDC_BD_CA_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_BD_CA_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3638,7 +4920,7 @@ static int wdc_get_ca_log_page(int fd, char *format) break; } else { - fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id); + fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = 0x%x\n", *cust_id); return -1; } break; @@ -3687,7 +4969,7 @@ static int wdc_get_c1_log_page(int fd, char *format, uint8_t interval) memset(data, 0, sizeof (__u8) * WDC_ADD_LOG_BUF_LEN); ret = nvme_get_log(fd, 0x01, WDC_NVME_ADD_LOG_OPCODE, false, - WDC_ADD_LOG_BUF_LEN, data); + NVME_NO_LOG_LSP, WDC_ADD_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); if (ret == 0) { @@ -3740,7 +5022,7 @@ static int wdc_get_d0_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_NVME_VU_SMART_LOG_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_VU_SMART_LOG_OPCODE, - false, WDC_NVME_VU_SMART_LOG_LEN, data); + false, NVME_NO_LOG_LSP, WDC_NVME_VU_SMART_LOG_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3763,23 +5045,33 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, const char *desc = "Retrieve additional performance statistics."; const char *interval = "Interval to read the statistics from [1, 15]."; int fd; + const char *log_page_version = "Log Page Version: 0 = vendor, 1 = WDC"; + const char *log_page_mask = "Log Page Mask, comma separated list: 0xC0, 0xC1, 0xCA, 0xD0"; int ret = 0; + int uuid_index = 0; + int page_mask = 0, num, i; + int log_page_list[16]; __u64 capabilities = 0; struct config { uint8_t interval; - int vendor_specific; char *output_format; + __u8 log_page_version; + char *log_page_mask; }; struct config cfg = { .interval = 14, .output_format = "normal", + .log_page_version = 0, + .log_page_mask = "", }; OPT_ARGS(opts) = { - OPT_UINT("interval", 'i', &cfg.interval, interval), - OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_UINT("interval", 'i', &cfg.interval, interval), + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), + OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), OPT_END() }; @@ -3787,6 +5079,50 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, if (fd < 0) return fd; + if (cfg.log_page_version == 0) { + uuid_index = 0; + } else if (cfg.log_page_version == 1) { + uuid_index = 1; + } else { + fprintf(stderr, "ERROR : WDC: unsupported log page version for this command\n"); + ret = -1; + goto out; + } + + num = argconfig_parse_comma_sep_array(cfg.log_page_mask, log_page_list, 16); + + if (num == -1) { + fprintf(stderr, "ERROR: WDC: log page list is malformed\n"); + ret = -1; + goto out; + } + + if (num == 0) + { + page_mask |= WDC_ALL_PAGE_MASK; + } + else + { + for (i = 0; i < num; i++) + { + if (log_page_list[i] == 0xc0) { + page_mask |= WDC_C0_PAGE_MASK; + } + if (log_page_list[i] == 0xc1) { + page_mask |= WDC_C1_PAGE_MASK; + } + if (log_page_list[i] == 0xca) { + page_mask |= WDC_CA_PAGE_MASK; + } + if (log_page_list[i] == 0xd0) { + page_mask |= WDC_D0_PAGE_MASK; + } + } + } + if (page_mask == 0) + fprintf(stderr, "ERROR : WDC: Unknown log page mask - %s\n", cfg.log_page_mask); + + capabilities = wdc_get_drive_capabilities(fd); if ((capabilities & WDC_DRIVE_CAP_SMART_LOG_MASK) == 0) { @@ -3795,25 +5131,78 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, goto out; } - if ((capabilities & (WDC_DRIVE_CAP_CA_LOG_PAGE)) == (WDC_DRIVE_CAP_CA_LOG_PAGE)) { + if (((capabilities & WDC_DRIVE_CAP_C0_LOG_PAGE) == WDC_DRIVE_CAP_C0_LOG_PAGE) && + (page_mask & WDC_C0_PAGE_MASK)) { + /* Get 0xC0 log page if possible. */ + ret = wdc_get_c0_log_page(fd, cfg.output_format, uuid_index); + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the C0 Log Page, ret = %d\n", ret); + } + if (((capabilities & (WDC_DRIVE_CAP_CA_LOG_PAGE)) == (WDC_DRIVE_CAP_CA_LOG_PAGE)) && + (page_mask & WDC_CA_PAGE_MASK)) { /* Get the CA Log Page */ ret = wdc_get_ca_log_page(fd, cfg.output_format); if (ret) fprintf(stderr, "ERROR : WDC : Failure reading the CA Log Page, ret = %d\n", ret); } - if ((capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE) == WDC_DRIVE_CAP_C1_LOG_PAGE) { + if (((capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE) == WDC_DRIVE_CAP_C1_LOG_PAGE) && + (page_mask & WDC_C1_PAGE_MASK)) { /* Get the C1 Log Page */ ret = wdc_get_c1_log_page(fd, cfg.output_format, cfg.interval); if (ret) fprintf(stderr, "ERROR : WDC : Failure reading the C1 Log Page, ret = %d\n", ret); } - if ((capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE) == WDC_DRIVE_CAP_D0_LOG_PAGE) { + if (((capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE) == WDC_DRIVE_CAP_D0_LOG_PAGE) && + (page_mask & WDC_D0_PAGE_MASK)) { /* Get the D0 Log Page */ ret = wdc_get_d0_log_page(fd, cfg.output_format); if (ret) fprintf(stderr, "ERROR : WDC : Failure reading the D0 Log Page, ret = %d\n", ret); } + out: + + return ret; +} + +static int wdc_do_clear_pcie_correctable_errors(int fd) +{ + int ret; + struct nvme_passthru_cmd admin_cmd; + + memset(&admin_cmd, 0, sizeof (admin_cmd)); + admin_cmd.opcode = WDC_NVME_CLEAR_PCIE_CORR_OPCODE; + admin_cmd.cdw12 = ((WDC_NVME_CLEAR_PCIE_CORR_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | + WDC_NVME_CLEAR_PCIE_CORR_CMD); + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_do_clear_pcie_correctable_errors_vuc(int fd) +{ + int ret; + struct nvme_passthru_cmd admin_cmd; + + memset(&admin_cmd, 0, sizeof (admin_cmd)); + admin_cmd.opcode = WDC_NVME_CLEAR_PCIE_CORR_OPCODE_VUC; + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_do_clear_pcie_correctable_errors_fid(int fd) +{ + int ret; + __u32 result; + __u32 value = 1 << 31; /* Bit 31 - clear PCIe correctable count */ + + ret = nvme_set_feature(fd, 0, WDC_NVME_CLEAR_PCIE_CORR_FEATURE_ID, value, + 0, 0, 0, NULL, &result); + + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); return ret; } @@ -3823,7 +5212,6 @@ static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct comma char *desc = "Clear PCIE Correctable Errors."; int fd, ret; __u64 capabilities = 0; - struct nvme_passthru_cmd admin_cmd; OPT_ARGS(opts) = { OPT_END() @@ -3839,28 +5227,32 @@ static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct comma } capabilities = wdc_get_drive_capabilities(fd); - if ((capabilities & WDC_DRIVE_CAP_CLEAR_PCIE) == 0) { + if ((capabilities & WDC_DRIVE_CAP_CLEAR_PCIE_MASK) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; goto out; } + + if (capabilities & WDC_DRIVE_CAP_CLEAR_PCIE) { + ret = wdc_do_clear_pcie_correctable_errors(fd); + } + else if (capabilities & WDC_DRIVE_CAP_VUC_CLEAR_PCIE) { + ret = wdc_do_clear_pcie_correctable_errors_vuc(fd); + } + else { + ret = wdc_do_clear_pcie_correctable_errors_fid(fd); + } - memset(&admin_cmd, 0, sizeof (admin_cmd)); - admin_cmd.opcode = WDC_NVME_CLEAR_PCIE_CORR_OPCODE; - admin_cmd.cdw12 = ((WDC_NVME_CLEAR_PCIE_CORR_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | - WDC_NVME_CLEAR_PCIE_CORR_CMD); - - ret = nvme_submit_admin_passthru(fd, &admin_cmd); - fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); out: return ret; } + static int wdc_drive_status(int argc, char **argv, struct command *command, struct plugin *plugin) { char *desc = "Get Drive Status."; int fd; - int ret = -1; + int ret = 0; __le32 system_eol_state; __le32 user_eol_state; __le32 format_corrupt_reason = cpu_to_le32(0xFFFFFFFF); @@ -4022,7 +5414,6 @@ static int wdc_get_fw_act_history(int fd, char *format) int fmt = -1; __u8 *data; struct wdc_fw_act_history_log_hdr *fw_act_history_hdr; - struct wdc_fw_act_history_log_entry *fw_act_history_entry; if (!wdc_check_device(fd)) return -1; @@ -4047,7 +5438,7 @@ static int wdc_get_fw_act_history(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID, - false, WDC_FW_ACT_HISTORY_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_FW_ACT_HISTORY_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -4055,17 +5446,85 @@ static int wdc_get_fw_act_history(int fd, char *format) if (ret == 0) { /* parse the data */ fw_act_history_hdr = (struct wdc_fw_act_history_log_hdr *)(data); - fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); - if (fw_act_history_hdr->num_entries > 0) - ret = wdc_print_fw_act_history_log(fw_act_history_entry, fw_act_history_hdr->num_entries, fmt); - else - fprintf(stderr, "INFO : WDC : No entries found in FW Activate History Log Page\n"); + 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); + else if (fw_act_history_hdr->num_entries == 0) { + fprintf(stderr, "INFO : WDC : No FW Activate History entries found.\n"); + ret = 0; + } + else { + fprintf(stderr, "ERROR : WDC : Invalid number entries found in FW Activate History Log Page - %d\n", fw_act_history_hdr->num_entries); + ret = -1; + } + } else { + fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n"); + ret = -1; + } + + free(data); + return ret; +} + +static int wdc_get_fw_act_history_C2(int fd, 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 num_entries = 0; + + if (!wdc_check_device(fd)) + return -1; + + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + return fmt; + } + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN); + + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID, + false, NVME_NO_LOG_LSP, WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + fw_act_history_log = (struct wdc_fw_act_history_log_format_c2*)(data); + num_entries = le32_to_cpu(fw_act_history_log->num_entries); + + if ((num_entries > 0) && (num_entries <= WDC_MAX_NUM_ACT_HIST_ENTRIES)) { + /* get the FW customer id */ + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&cust_id)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + ret = -1; + goto freeData; + } + + ret = wdc_print_fw_act_history_log(data, num_entries, fmt, *cust_id); + } else if (num_entries == 0) { + fprintf(stderr, "INFO : WDC : No FW Activate History entries found.\n"); + ret = 0; + } + else { + fprintf(stderr, "ERROR : WDC : Invalid number entries found in FW Activate History Log Page - %d\n", num_entries); + ret = -1; + } } else { fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n"); ret = -1; } +freeData: free(data); return ret; } @@ -4098,16 +5557,86 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *com return fd; capabilities = wdc_get_drive_capabilities(fd); - - if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) == WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { - ret = wdc_get_fw_act_history(fd, cfg.output_format); - if (ret) - fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); - } else { + if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_MASK) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; + goto out; } + if (capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { + int uuid_index = 0; + bool c0GuidMatch = false; + __u8 *data; + int i; + + /* check for the GUID in the 0xC0 log page to determine which log page to use to */ + /* to retrieve fw activate history data */ + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE, + NVME_NO_LOG_LSP, 0, 0, false, uuid_index, WDC_NVME_SMART_CLOUD_ATTR_LEN, data); + + if (ret == 0) { + /* Verify GUID matches */ + for (i=0; i<16; i++) { + if (scao_guid[i] != data[SCAO_LPG + i]) { + c0GuidMatch = false; + break; + } + } + + if (i == 16) { + c0GuidMatch = true; + } + } + + free(data); + if (c0GuidMatch) { + ret = wdc_get_fw_act_history_C2(fd, cfg.output_format); + } + else { + ret = wdc_get_fw_act_history(fd, cfg.output_format); + } + } else { + ret = wdc_get_fw_act_history_C2(fd, cfg.output_format); + } + + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); +out: + return ret; +} + +static int wdc_do_clear_fw_activate_history_vuc(int fd) +{ + int ret = -1; + struct nvme_passthru_cmd admin_cmd; + + memset(&admin_cmd, 0, sizeof (admin_cmd)); + admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE; + admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | + WDC_NVME_CLEAR_FW_ACT_HIST_CMD); + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + return ret; +} + +static int wdc_do_clear_fw_activate_history_fid(int fd) +{ + int ret = -1; + __u32 result; + __u32 value = 1 << 31; /* Bit 31 - Clear Firmware Update History Log */ + + ret = nvme_set_feature(fd, 0, WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID, value, + 0, 0, 0, NULL, &result); + + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); return ret; } @@ -4118,7 +5647,6 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command * int fd; int ret = -1; __u64 capabilities = 0; - struct nvme_passthru_cmd admin_cmd; OPT_ARGS(opts) = { OPT_END() @@ -4129,19 +5657,18 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command * return fd; capabilities = wdc_get_drive_capabilities(fd); - if ((capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) != WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) { + if ((capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY_MASK) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; goto out; } - memset(&admin_cmd, 0, sizeof (admin_cmd)); - admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE; - admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | - WDC_NVME_CLEAR_FW_ACT_HIST_CMD); - - ret = nvme_submit_admin_passthru(fd, &admin_cmd); - fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + if (capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) { + ret = wdc_do_clear_fw_activate_history_vuc(fd); + } + else { + ret = wdc_do_clear_fw_activate_history_fid(fd); + } out: return ret; @@ -4811,7 +6338,7 @@ static int wdc_do_drive_essentials(int fd, char *dir, char *key) memset(dataBuffer, 0, dataBufferSize); ret = nvme_get_log(fd, WDC_DE_GLOBAL_NSID, deVULogPagesList[vuLogIdx].logPageId, - false, dataBufferSize, dataBuffer); + false, NVME_NO_LOG_LSP, dataBufferSize, dataBuffer); if (ret) { fprintf(stderr, "ERROR : WDC : nvme_get_log() for log page 0x%x failed, ret = %d\n", deVULogPagesList[vuLogIdx].logPageId, ret); @@ -5025,7 +6552,6 @@ static int wdc_do_drive_info(int fd, __u32 *result) return ret; } - static int wdc_drive_resize(int argc, char **argv, struct command *command, struct plugin *plugin) { @@ -5230,13 +6756,16 @@ static const char *nvme_log_id_to_string(__u8 log_id) case NVME_LOG_SANITIZE: return "Sanitize Status Log ID"; case WDC_LOG_ID_C0: return "WDC Vendor Unique Log ID"; + case WDC_LOG_ID_C1: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C2: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C4: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C5: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C6: return "WDC Vendor Unique Log ID"; + case WDC_LOG_ID_C8: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_CA: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_CB: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D0: return "WDC Vendor Unique Log ID"; + case WDC_LOG_ID_D1: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D6: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D7: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D8: return "WDC Vendor Unique Log ID"; @@ -5258,7 +6787,9 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command int ret = 0; __u64 capabilities = 0; struct wdc_c2_cbs_data *cbs_data = NULL; - int i; + int i; + __u8 log_id = 0; + __u32 device_id, read_vendor_id; struct config { char *output_format; @@ -5288,10 +6819,14 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command if ((capabilities & WDC_DRIVE_CAP_LOG_PAGE_DIR) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; - } else { + } + else { + ret = wdc_get_pci_ids(&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(fd, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE) == false) { - fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page not supported\n", __func__); + if (wdc_nvme_check_supported_log_page(fd, log_id) == false) { + fprintf(stderr, "%s: ERROR : WDC : 0x%x Log Page not supported\n", __func__, log_id); ret = -1; goto out; } @@ -5491,51 +7026,349 @@ static int wdc_dump_telemetry_hdr(int fd, int log_id, struct nvme_telemetry_log_ return ret; } -static void wdc_print_nand_stats_normal(struct wdc_nand_stats *data) -{ - printf(" NAND Statistics :- \n"); - printf(" NAND Writes TLC (Bytes) %.0Lf\n", - int128_to_double(data->nand_write_tlc)); - printf(" NAND Writes SLC (Bytes) %.0Lf\n", - int128_to_double(data->nand_write_slc)); - printf(" NAND Program Failures %"PRIu32"\n", - (uint32_t)le32_to_cpu(data->nand_prog_failure)); - printf(" NAND Erase Failures %"PRIu32"\n", - (uint32_t)le32_to_cpu(data->nand_erase_failure)); - printf(" Bad Block Count %"PRIu32"\n", - (uint32_t)le32_to_cpu(data->bad_block_count)); - printf(" NAND XOR/RAID Recovery Trigger Events %"PRIu64"\n", - le64_to_cpu(data->nand_rec_trigger_event)); - printf(" E2E Error Counter %"PRIu64"\n", - le64_to_cpu(data->e2e_error_counter)); - printf(" Number Successful NS Resizing Events %"PRIu64"\n", - le64_to_cpu(data->successful_ns_resize_event)); +static void wdc_print_nand_stats_normal(__u16 version, void *data) +{ + struct wdc_nand_stats *nand_stats = (struct wdc_nand_stats *)(data); + struct wdc_nand_stats_V3 *nand_stats_v3 = (struct wdc_nand_stats_V3 *)(data); + __u64 temp_raw; + __u16 temp_norm; + __u64 *temp_ptr = NULL; + + switch (version) + { + case 0: + printf(" NAND Statistics :- \n"); + printf(" NAND Writes TLC (Bytes) %.0Lf\n", + int128_to_double(nand_stats->nand_write_tlc)); + printf(" NAND Writes SLC (Bytes) %.0Lf\n", + int128_to_double(nand_stats->nand_write_slc)); + printf(" NAND Program Failures %"PRIu32"\n", + (uint32_t)le32_to_cpu(nand_stats->nand_prog_failure)); + printf(" NAND Erase Failures %"PRIu32"\n", + (uint32_t)le32_to_cpu(nand_stats->nand_erase_failure)); + printf(" Bad Block Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(nand_stats->bad_block_count)); + printf(" NAND XOR/RAID Recovery Trigger Events %"PRIu64"\n", + le64_to_cpu(nand_stats->nand_rec_trigger_event)); + printf(" E2E Error Counter %"PRIu64"\n", + le64_to_cpu(nand_stats->e2e_error_counter)); + printf(" Number Successful NS Resizing Events %"PRIu64"\n", + le64_to_cpu(nand_stats->successful_ns_resize_event)); + printf(" log page version %"PRIu16"\n", + le16_to_cpu(nand_stats->log_page_version)); + break; + case 3: + printf(" NAND Statistics V3:- \n"); + printf(" TLC Units Written %.0Lf\n", + int128_to_double(nand_stats_v3->nand_write_tlc)); + printf(" SLC Units Written %.0Lf\n", + int128_to_double(nand_stats_v3->nand_write_slc)); + temp_ptr = (__u64 *)nand_stats_v3->bad_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Bad NAND Blocks Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Bad NAND Blocks Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + printf(" NAND XOR Recovery count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->xor_recovery_count)); + printf(" UECC Read Error count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->uecc_read_error_count)); + printf(" SSD End to End corrected errors %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->ssd_correction_counts[0])); + printf(" SSD End to End detected errors %"PRIu32"\n", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[8])); + printf(" SSD End to End uncorrected E2E errors %"PRIu32"\n", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[12])); + printf(" System data %% life-used %u\n", + nand_stats_v3->percent_life_used); + printf(" User Data Erase Counts - TLC Min %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[0])); + printf(" User Data Erase Counts - TLC Max %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[1])); + printf(" User Data Erase Counts - SLC Min %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[2])); + printf(" User Data Erase Counts - SLC Max %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[3])); + temp_ptr = (__u64 *)nand_stats_v3->program_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Program Fail Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Program Fail Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + temp_ptr = (__u64 *)nand_stats_v3->erase_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Erase Fail Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Erase Fail Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + printf(" PCIe Correctable Error Count %"PRIu16"\n", + le16_to_cpu(nand_stats_v3->correctable_error_count)); + printf(" %% Free Blocks (User) %u\n", + nand_stats_v3->percent_free_blocks_user); + printf(" Security Version Number %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->security_version_number)); + printf(" %% Free Blocks (System) %u\n", + nand_stats_v3->percent_free_blocks_system); + printf(" Data Set Management Commands %.0Lf\n", + int128_to_double(nand_stats_v3->trim_completions)); + printf(" Estimate of Incomplete Trim Data %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->trim_completions[16])); + printf(" %% of completed trim %u\n", + nand_stats_v3->trim_completions[24]); + printf(" Background Back-Pressure-Guage %u\n", + nand_stats_v3->back_pressure_guage); + printf(" Soft ECC Error Count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->soft_ecc_error_count)); + printf(" Refresh Count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->refresh_count)); + temp_ptr = (__u64 *)nand_stats_v3->bad_sys_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Bad System Nand Block Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Bad System Nand Block Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + printf(" Endurance Estimate %.0Lf\n", + int128_to_double(nand_stats_v3->endurance_estimate)); + printf(" Thermal Throttling Count %u\n", + nand_stats_v3->thermal_throttling_st_ct[0]); + printf(" Thermal Throttling Status %u\n", + nand_stats_v3->thermal_throttling_st_ct[1]); + printf(" Unaligned I/O %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->unaligned_IO)); + printf(" Physical Media Units Read %.0Lf\n", + int128_to_double(nand_stats_v3->physical_media_units)); + printf(" log page version %"PRIu16"\n", + le16_to_cpu(nand_stats_v3->log_page_version)); + break; + + default: + fprintf(stderr, "WDC: Nand Stats ERROR : Invalid version\n"); + break; + + } } -static void wdc_print_nand_stats_json(struct wdc_nand_stats *data) +static void wdc_print_nand_stats_json(__u16 version, void *data) { + struct wdc_nand_stats *nand_stats = (struct wdc_nand_stats *)(data); + struct wdc_nand_stats_V3 *nand_stats_v3 = (struct wdc_nand_stats_V3 *)(data); struct json_object *root; + root = json_create_object(); + __u64 temp_raw; + __u16 temp_norm; + __u64 *temp_ptr = NULL; + + switch (version) + { + + case 0: + + json_object_add_value_float(root, "NAND Writes TLC (Bytes)", + int128_to_double(nand_stats->nand_write_tlc)); + json_object_add_value_float(root, "NAND Writes SLC (Bytes)", + int128_to_double(nand_stats->nand_write_slc)); + json_object_add_value_uint(root, "NAND Program Failures", + le32_to_cpu(nand_stats->nand_prog_failure)); + json_object_add_value_uint(root, "NAND Erase Failures", + le32_to_cpu(nand_stats->nand_erase_failure)); + json_object_add_value_uint(root, "Bad Block Count", + le32_to_cpu(nand_stats->bad_block_count)); + json_object_add_value_uint(root, "NAND XOR/RAID Recovery Trigger Events", + le64_to_cpu(nand_stats->nand_rec_trigger_event)); + json_object_add_value_uint(root, "E2E Error Counter", + le64_to_cpu(nand_stats->e2e_error_counter)); + json_object_add_value_uint(root, "Number Successful NS Resizing Events", + le64_to_cpu(nand_stats->successful_ns_resize_event)); + + json_print_object(root, NULL); + printf("\n"); + break; + + case 3: + + json_object_add_value_float(root, "NAND Writes TLC (Bytes)", + int128_to_double(nand_stats_v3->nand_write_tlc)); + json_object_add_value_float(root, "NAND Writes SLC (Bytes)", + int128_to_double(nand_stats_v3->nand_write_slc)); + temp_ptr = (__u64 *)nand_stats_v3->bad_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Bad NAND Blocks Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Bad NAND Blocks Count - Raw", + le64_to_cpu(temp_raw)); + json_object_add_value_uint(root, "NAND XOR Recovery count", + le64_to_cpu(nand_stats_v3->xor_recovery_count)); + json_object_add_value_uint(root, "UECC Read Error count", + le64_to_cpu(nand_stats_v3->uecc_read_error_count)); + json_object_add_value_uint(root, "SSD End to End corrected errors", + le64_to_cpu(nand_stats_v3->ssd_correction_counts[0])); + json_object_add_value_uint(root, "SSD End to End detected errors", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[8])); + json_object_add_value_uint(root, "SSD End to End uncorrected E2E errors", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[12])); + json_object_add_value_uint(root, "System data % life-used", + nand_stats_v3->percent_life_used); + json_object_add_value_uint(root, "User Data Erase Counts - SLC Min", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[0])); + json_object_add_value_uint(root, "User Data Erase Counts - SLC Max", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[1])); + json_object_add_value_uint(root, "User Data Erase Counts - TLC Min", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[2])); + json_object_add_value_uint(root, "User Data Erase Counts - TLC Max", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[3])); + temp_ptr = (__u64 *)nand_stats_v3->program_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Program Fail Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Program Fail Count - Raw", + le64_to_cpu(temp_raw)); + temp_ptr = (__u64 *)nand_stats_v3->erase_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Erase Fail Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Erase Fail Count - Raw", + le64_to_cpu(temp_raw)); + json_object_add_value_uint(root, "PCIe Correctable Error Count", + le16_to_cpu(nand_stats_v3->correctable_error_count)); + json_object_add_value_uint(root, "% Free Blocks (User)", + nand_stats_v3->percent_free_blocks_user); + json_object_add_value_uint(root, "Security Version Number", + le64_to_cpu(nand_stats_v3->security_version_number)); + json_object_add_value_uint(root, "% Free Blocks (System)", + nand_stats_v3->percent_free_blocks_system); + json_object_add_value_float(root, "Data Set Management Commands", + int128_to_double(nand_stats_v3->trim_completions)); + json_object_add_value_uint(root, "Estimate of Incomplete Trim Data", + le64_to_cpu(nand_stats_v3->trim_completions[16])); + json_object_add_value_uint(root, "%% of completed trim", + nand_stats_v3->trim_completions[24]); + json_object_add_value_uint(root, "Background Back-Pressure-Guage", + nand_stats_v3->back_pressure_guage); + json_object_add_value_uint(root, "Soft ECC Error Count", + le64_to_cpu(nand_stats_v3->soft_ecc_error_count)); + json_object_add_value_uint(root, "Refresh Count", + le64_to_cpu(nand_stats_v3->refresh_count)); + temp_ptr = (__u64 *)nand_stats_v3->bad_sys_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Bad System Nand Block Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Bad System Nand Block Count - Raw", + le64_to_cpu(temp_raw)); + json_object_add_value_float(root, "Endurance Estimate", + int128_to_double(nand_stats_v3->endurance_estimate)); + json_object_add_value_uint(root, "Thermal Throttling Status", + nand_stats_v3->thermal_throttling_st_ct[0]); + json_object_add_value_uint(root, "Thermal Throttling Count", + nand_stats_v3->thermal_throttling_st_ct[1]); + json_object_add_value_uint(root, "Unaligned I/O", + le64_to_cpu(nand_stats_v3->unaligned_IO)); + json_object_add_value_float(root, "Physical Media Units Read", + int128_to_double(nand_stats_v3->physical_media_units)); + json_object_add_value_uint(root, "log page version", + le16_to_cpu(nand_stats_v3->log_page_version)); + + json_print_object(root, NULL); + printf("\n"); + break; + + default: + printf("%s: Invalid Stats Version = %d\n", __func__, version); + break; + + } + + json_free_object(root); + +} + +static void wdc_print_pcie_stats_normal(struct wdc_vs_pcie_stats *pcie_stats) +{ + printf(" PCIE Statistics :- \n"); + printf(" Unsupported Request Error Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->unsupportedRequestErrorCount)); + printf(" ECRC Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->ecrcErrorStatusCount)); + printf(" Malformed TLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->malformedTlpStatusCount)); + printf(" Receiver Overflow Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->receiverOverflowStatusCount)); + printf(" Unexpected Completion Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->unexpectedCmpltnStatusCount)); + printf(" Complete Abort Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->completeAbortStatusCount)); + printf(" Completion Timeout Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->cmpltnTimoutStatusCount)); + printf(" Flow Control Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->flowControlErrorStatusCount)); + printf(" Poisoned TLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->poisonedTlpStatusCount)); + printf(" Dlink Protocol Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->dLinkPrtclErrorStatusCount)); + printf(" Advisory Non Fatal Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->advsryNFatalErrStatusCount)); + printf(" Replay Timer TO Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->replayTimerToStatusCount)); + printf(" Replay Number Rollover Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->replayNumRolloverStCount)); + printf(" Bad DLLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->badDllpStatusCount)); + printf(" Bad TLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->badTlpStatusCount)); + printf(" Receiver Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->receiverErrStatusCount)); + +} +static void wdc_print_pcie_stats_json(struct wdc_vs_pcie_stats *pcie_stats) +{ + struct json_object *root; root = json_create_object(); - json_object_add_value_float(root, "NAND Writes TLC (Bytes)", - int128_to_double(data->nand_write_tlc)); - json_object_add_value_float(root, "NAND Writes SLC (Bytes)", - int128_to_double(data->nand_write_slc)); - json_object_add_value_uint(root, "NAND Program Failures", - le32_to_cpu(data->nand_prog_failure)); - json_object_add_value_uint(root, "NAND Erase Failures", - le32_to_cpu(data->nand_erase_failure)); - json_object_add_value_uint(root, "Bad Block Count", - le32_to_cpu(data->bad_block_count)); - json_object_add_value_uint(root, "NAND XOR/RAID Recovery Trigger Events", - le64_to_cpu(data->nand_rec_trigger_event)); - json_object_add_value_uint(root, "E2E Error Counter", - le64_to_cpu(data->e2e_error_counter)); - json_object_add_value_uint(root, "Number Successful NS Resizing Events", - le64_to_cpu(data->successful_ns_resize_event)); + + json_object_add_value_uint(root, "Unsupported Request Error Counter", + le64_to_cpu(pcie_stats->unsupportedRequestErrorCount)); + json_object_add_value_uint(root, "ECRC Error Status Counter", + le64_to_cpu(pcie_stats->ecrcErrorStatusCount)); + json_object_add_value_uint(root, "Malformed TLP Status Counter", + le64_to_cpu(pcie_stats->malformedTlpStatusCount)); + + json_object_add_value_uint(root, "Receiver Overflow Status Counter", + le64_to_cpu(pcie_stats->receiverOverflowStatusCount)); + json_object_add_value_uint(root, "Unexpected Completion Status Counter", + le64_to_cpu(pcie_stats->unexpectedCmpltnStatusCount)); + json_object_add_value_uint(root, "Complete Abort Status Counter", + le64_to_cpu(pcie_stats->completeAbortStatusCount)); + json_object_add_value_uint(root, "Completion Timeout Status Counter", + le64_to_cpu(pcie_stats->cmpltnTimoutStatusCount)); + json_object_add_value_uint(root, "Flow Control Error Status Counter", + le64_to_cpu(pcie_stats->flowControlErrorStatusCount)); + json_object_add_value_uint(root, "Poisoned TLP Status Counter", + le64_to_cpu(pcie_stats->poisonedTlpStatusCount)); + json_object_add_value_uint(root, "Dlink Protocol Error Status Counter", + le64_to_cpu(pcie_stats->dLinkPrtclErrorStatusCount)); + json_object_add_value_uint(root, "Advisory Non Fatal Error Status Counter", + le64_to_cpu(pcie_stats->advsryNFatalErrStatusCount)); + json_object_add_value_uint(root, "Replay Timer TO Status Counter", + le64_to_cpu(pcie_stats->replayTimerToStatusCount)); + json_object_add_value_uint(root, "Replay Number Rollover Status Counter", + le64_to_cpu(pcie_stats->replayNumRolloverStCount)); + json_object_add_value_uint(root, "Bad DLLP Status Counter", + le64_to_cpu(pcie_stats->badDllpStatusCount)); + json_object_add_value_uint(root, "Bad TLP Status Counter", + le64_to_cpu(pcie_stats->badTlpStatusCount)); + json_object_add_value_uint(root, "Receiver Error Status Counter", + le64_to_cpu(pcie_stats->receiverErrStatusCount)); json_print_object(root, NULL); printf("\n"); + json_free_object(root); } @@ -5544,7 +7377,7 @@ static int wdc_do_vs_nand_stats(int fd, char *format) int ret; int fmt = -1; uint8_t *output = NULL; - struct wdc_nand_stats *nand_stats; + __u16 version = 0; if ((output = (uint8_t*)calloc(WDC_NVME_NAND_STATS_SIZE, sizeof(uint8_t))) == NULL) { fprintf(stderr, "ERROR : WDC : calloc : %s\n", strerror(errno)); @@ -5553,7 +7386,7 @@ static int wdc_do_vs_nand_stats(int fd, char *format) } ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_NAND_STATS_LOG_ID, - false, WDC_NVME_NAND_STATS_SIZE, (void*)output); + false, NVME_NO_LOG_LSP, WDC_NVME_NAND_STATS_SIZE, (void*)output); if (ret) { fprintf(stderr, "ERROR : WDC : %s : Failed to retreive NAND stats\n", __func__); goto out; @@ -5565,14 +7398,15 @@ static int wdc_do_vs_nand_stats(int fd, char *format) goto out; } + version = output[WDC_NVME_NAND_STATS_SIZE - 2]; + /* parse the data */ - nand_stats = (struct wdc_nand_stats *)(output); switch (fmt) { case NORMAL: - wdc_print_nand_stats_normal(nand_stats); + wdc_print_nand_stats_normal(version, output); break; case JSON: - wdc_print_nand_stats_json(nand_stats); + wdc_print_nand_stats_json(version, output); break; } } @@ -5586,6 +7420,7 @@ static int wdc_vs_nand_stats(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Retrieve NAND statistics."; + int fd; int ret = 0; __u64 capabilities = 0; @@ -5621,6 +7456,97 @@ static int wdc_vs_nand_stats(int argc, char **argv, struct command *command, return ret; } +static int wdc_do_vs_pcie_stats(int fd, + struct wdc_vs_pcie_stats *pcieStatsPtr) +{ + int ret; + struct nvme_admin_cmd admin_cmd; + int pcie_stats_size = sizeof(struct wdc_vs_pcie_stats); + + memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd)); + admin_cmd.opcode = WDC_NVME_PCIE_STATS_OPCODE; + admin_cmd.addr = (__u64)(uintptr_t)pcieStatsPtr; + admin_cmd.data_len = pcie_stats_size; + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + + return ret; +} + +static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + const char *desc = "Retrieve PCIE statistics."; + + int fd; + int ret = 0; + __u64 capabilities = 0; + int fmt = -1; + struct wdc_vs_pcie_stats *pcieStatsPtr = NULL; + int pcie_stats_size = sizeof(struct wdc_vs_pcie_stats); + bool huge; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + ret = fmt; + goto out; + } + + pcieStatsPtr = nvme_alloc(pcie_stats_size, &huge); + if (pcieStatsPtr == NULL) { + fprintf(stderr, "ERROR : WDC : PCIE Stats alloc : %s\n", strerror(errno)); + ret = -1; + goto out; + } + + memset((void *)pcieStatsPtr, 0, pcie_stats_size); + + capabilities = wdc_get_drive_capabilities(fd); + + if ((capabilities & WDC_DRIVE_CAP_PCIE_STATS) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + } else { + ret = wdc_do_vs_pcie_stats(fd, pcieStatsPtr); + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading PCIE statistics, ret = 0x%x\n", ret); + else { + /* parse the data */ + switch (fmt) { + case NORMAL: + wdc_print_pcie_stats_normal(pcieStatsPtr); + break; + case JSON: + wdc_print_pcie_stats_json(pcieStatsPtr); + break; + } + } + } + + nvme_free(pcieStatsPtr, huge); + +out: + return ret; +} + static int wdc_vs_drive_info(int argc, char **argv, struct command *command, struct plugin *plugin) { @@ -5630,8 +7556,24 @@ static int wdc_vs_drive_info(int argc, char **argv, __le32 result; __u16 size; double rev; + struct nvme_id_ctrl ctrl; + char vsData[32] = {0}; + char major_rev = 0, minor_rev = 0; + int fmt = -1; + struct json_object *root = NULL; + char formatter[41] = { 0 }; + char rev_str[8] = { 0 }; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), OPT_END() }; @@ -5639,25 +7581,558 @@ static int wdc_vs_drive_info(int argc, char **argv, if (fd < 0) return fd; + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC %s invalid output format\n", __func__); + return fmt; + } + + /* get the id ctrl data used to fill in drive info below */ + ret = nvme_identify_ctrl(fd, &ctrl); + + if (ret) { + fprintf(stderr, "ERROR : WDC %s: Identify Controller failed\n", __func__); + return ret; + } + wdc_check_device(fd); capabilities = wdc_get_drive_capabilities(fd); if ((capabilities & WDC_DRIVE_CAP_INFO) == WDC_DRIVE_CAP_INFO) { ret = wdc_do_drive_info(fd, &result); + + if (!ret) { + size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16); + rev = (double)(cpu_to_le32(result) & 0x0000ffff); + + if (fmt == NORMAL) { + printf("Drive HW Revison: %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_int(root, "FTL Unit Size", le16_to_cpu(size)); + wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], sizeof(ctrl.sn)); + json_object_add_value_string(root, "Customer SN", formatter); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } + } + } + else if ((capabilities & WDC_DRIVE_CAP_INFO_2) == WDC_DRIVE_CAP_INFO_2) { + memcpy(vsData, &ctrl.vs[0], 32); + + major_rev = ctrl.sn[12]; + minor_rev = ctrl.sn[13]; + + if (fmt == NORMAL) { + printf("Drive HW Revision: %c.%c \n", major_rev, minor_rev); + printf("Customer SN: %-.*s\n", 14, &ctrl.sn[0]); + } + else if (fmt == JSON) { + root = json_create_object(); + sprintf(rev_str, "%c.%c", major_rev, minor_rev); + json_object_add_value_string(root, "Drive HW Revison", rev_str); + wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], 14); + json_object_add_value_string(root, "Customer SN", formatter); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } } else { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); - ret = -1; + return -1; } - if (!ret) { - size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16); - rev = (double)(cpu_to_le32(result) & 0x0000ffff); - printf("Drive HW Revison: %4.1f\n", (.1 * rev)); - printf("FTL Unit Size: 0x%x KB\n", size); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_vs_temperature_stats(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Send a vs-temperature-stats command."; + struct nvme_smart_log smart_log; + struct nvme_id_ctrl id_ctrl; + uint64_t capabilities = 0; + __u32 hctm_tmt; + int fd, ret; + int temperature, temp_tmt1, temp_tmt2; + int fmt = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + ret = fmt; + goto END; } + /* check if command is supported */ + wdc_check_device(fd); + capabilities = wdc_get_drive_capabilities(fd); + if ((capabilities & WDC_DRIVE_CAP_TEMP_STATS) != WDC_DRIVE_CAP_TEMP_STATS) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + return -1; + } + + /* get the temperature stats or report errors */ + ret = nvme_identify_ctrl(fd, &id_ctrl); + if (ret != 0) + goto END; + ret = nvme_smart_log(fd, NVME_NSID_ALL, &smart_log); + if (ret != 0) + goto END; + + /* convert from Kelvin to degrees Celsius */ + temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]) - 273; + + /* retrieve HCTM Thermal Management Temperatures */ + nvme_get_feature(fd, 0, 0x10, 0, 0, 0, 0, &hctm_tmt); + temp_tmt1 = ((hctm_tmt >> 16) & 0xffff) ? ((hctm_tmt >> 16) & 0xffff) - 273 : 0; + temp_tmt2 = (hctm_tmt & 0xffff) ? (hctm_tmt & 0xffff) - 273 : 0; + + if (fmt == NORMAL) { + /* print the temperature stats */ + printf("Temperature Stats for NVME device:%s namespace-id:%x\n", + devicename, WDC_DE_GLOBAL_NSID); + + printf("Current Composite Temperature : %d °C\n", temperature); + printf("WCTEMP : %"PRIu16" °C\n", id_ctrl.wctemp - 273); + printf("CCTEMP : %"PRIu16" °C\n", id_ctrl.cctemp - 273); + printf("DITT support : 0\n"); + printf("HCTM support : %"PRIu16"\n", id_ctrl.hctma); + + printf("HCTM Light (TMT1) : %"PRIu16" °C\n", temp_tmt1); + printf("TMT1 Transition Counter : %"PRIu32"\n", smart_log.thm_temp1_trans_count); + printf("TMT1 Total Time : %"PRIu32"\n", smart_log.thm_temp1_total_time); + + printf("HCTM Heavy (TMT2) : %"PRIu16" °C\n", temp_tmt2); + printf("TMT2 Transition Counter : %"PRIu32"\n", smart_log.thm_temp2_trans_count); + printf("TMT2 Total Time : %"PRIu32"\n", smart_log.thm_temp2_total_time); + printf("Thermal Shutdown Threshold : 95 °C\n"); + } + else if (fmt == JSON) { + struct json_object *root; + root = json_create_object(); + + json_object_add_value_int(root, "Current Composite Temperature", le32_to_cpu(temperature)); + json_object_add_value_int(root, "WCTEMP", le16_to_cpu(id_ctrl.wctemp - 273)); + json_object_add_value_int(root, "CCTEMP", le16_to_cpu(id_ctrl.cctemp - 273)); + json_object_add_value_int(root, "DITT support", 0); + json_object_add_value_int(root, "HCTM support", le16_to_cpu(id_ctrl.hctma)); + + json_object_add_value_int(root, "HCTM Light (TMT1)", le16_to_cpu(temp_tmt1)); + json_object_add_value_int(root, "TMT1 Transition Counter", le32_to_cpu(smart_log.thm_temp1_trans_count)); + json_object_add_value_int(root, "TMT1 Total Time", le32_to_cpu(smart_log.thm_temp1_total_time)); + + json_object_add_value_int(root, "HCTM Light (TMT2)", le16_to_cpu(temp_tmt2)); + json_object_add_value_int(root, "TMT2 Transition Counter", le32_to_cpu(smart_log.thm_temp2_trans_count)); + json_object_add_value_int(root, "TMT2 Total Time", le32_to_cpu(smart_log.thm_temp2_total_time)); + json_object_add_value_int(root, "Thermal Shutdown Threshold", 95); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } + else + printf("%s: Invalid format\n", __func__); + +END: fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); return ret; } +static int wdc_capabilities(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Send a capabilities command."; + uint64_t capabilities = 0; + int fd; + + OPT_ARGS(opts) = + { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + /* get capabilities */ + wdc_check_device(fd); + capabilities = wdc_get_drive_capabilities(fd); + + /* print command and supported status */ + printf("WDC Plugin Capabilities for NVME device:%s\n", devicename); + printf("cap-diag : %s\n", + capabilities & WDC_DRIVE_CAP_CAP_DIAG ? "Supported" : "Not Supported"); + printf("drive-log : %s\n", + capabilities & WDC_DRIVE_CAP_DRIVE_LOG ? "Supported" : "Not Supported"); + printf("get-crash-dump : %s\n", + capabilities & WDC_DRIVE_CAP_CRASH_DUMP ? "Supported" : "Not Supported"); + printf("get-pfail-dump : %s\n", + capabilities & WDC_DRIVE_CAP_PFAIL_DUMP ? "Supported" : "Not Supported"); + printf("id-ctrl : Supported\n"); + printf("purge : Supported\n"); + printf("purge-monitor : Supported\n"); + printf("vs-internal-log : %s\n", + capabilities & WDC_DRIVE_CAP_INTERNAL_LOG_MASK ? "Supported" : "Not Supported"); + printf("vs-nand-stats : %s\n", + capabilities & WDC_DRIVE_CAP_NAND_STATS ? "Supported" : "Not Supported"); + printf("vs-smart-add-log : %s\n", + capabilities & WDC_DRIVE_CAP_SMART_LOG_MASK ? "Supported" : "Not Supported"); + printf("--C0 Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_C0_LOG_PAGE ? "Supported" : "Not Supported"); + printf("--C1 Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE ? "Supported" : "Not Supported"); + printf("--CA Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_CA_LOG_PAGE ? "Supported" : "Not Supported"); + printf("--D0 Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE ? "Supported" : "Not Supported"); + printf("clear-pcie-correctable-errors : %s\n", + capabilities & WDC_DRIVE_CAP_CLEAR_PCIE_MASK ? "Supported" : "Not Supported"); + printf("drive-essentials : %s\n", + capabilities & WDC_DRIVE_CAP_DRIVE_ESSENTIALS ? "Supported" : "Not Supported"); + printf("get-drive-status : %s\n", + capabilities & WDC_DRIVE_CAP_DRIVE_STATUS ? "Supported" : "Not Supported"); + printf("clear-assert-dump : %s\n", + capabilities & WDC_DRIVE_CAP_CLEAR_ASSERT ? "Supported" : "Not Supported"); + printf("drive-resize : %s\n", + capabilities & WDC_DRIVE_CAP_RESIZE ? "Supported" : "Not Supported"); + printf("vs-fw-activate-history : %s\n", + capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_MASK ? "Supported" : "Not Supported"); + printf("clear-fw-activate-history : %s\n", + capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY_MASK ? "Supported" : "Not Supported"); + printf("vs-telemetry-controller-option: %s\n", + capabilities & WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG ? "Supported" : "Not Supported"); + printf("vs-error-reason-identifier : %s\n", + capabilities & WDC_DRIVE_CAP_REASON_ID ? "Supported" : "Not Supported"); + printf("log-page-directory : %s\n", + capabilities & WDC_DRIVE_CAP_LOG_PAGE_DIR ? "Supported" : "Not Supported"); + printf("namespace-resize : %s\n", + capabilities & WDC_DRIVE_CAP_NS_RESIZE ? "Supported" : "Not Supported"); + printf("vs-drive-info : %s\n", + capabilities & (WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_INFO_2) ? "Supported" : "Not Supported"); + printf("vs-temperature-stats : %s\n", + capabilities & WDC_DRIVE_CAP_TEMP_STATS ? "Supported" : "Not Supported"); + printf("cloud-SSD-plugin-version : %s\n", + capabilities & WDC_DRIVE_CAP_CLOUD_SSD_VERSION ? "Supported" : "Not Supported"); + printf("vs-pcie-stats : %s\n", + capabilities & WDC_DRIVE_CAP_PCIE_STATS ? "Supported" : "Not Supported"); + printf("capabilities : Supported\n"); + return 0; +} + +static int wdc_cloud_ssd_plugin_version(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Get Cloud SSD Plugin Version command."; + uint64_t capabilities = 0; + int fd; + + OPT_ARGS(opts) = + { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + /* get capabilities */ + wdc_check_device(fd); + capabilities = wdc_get_drive_capabilities(fd); + + if ((capabilities & WDC_DRIVE_CAP_CLOUD_SSD_VERSION) == WDC_DRIVE_CAP_CLOUD_SSD_VERSION) { + /* print command and supported status */ + printf("WDC Cloud SSD Plugin Version: 1.0\n"); + } else { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + } + + return 0; +} + +static int wdc_enc_get_log(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + char *desc = "Get Enclosure Log."; + char *file = "Output file pathname."; + char *size = "Data retrieval transfer size."; + char *log = "Enclosure Log Page ID."; + FILE *output_fd; + int xfer_size = 0; + int fd; + int len; + int err; + + struct config { + char *file; + __u32 xfer_size; + __u32 log_id; + }; + + struct config cfg = { + .file = NULL, + .xfer_size = 0, + .log_id = 0xffffffff, + }; + + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'o', &cfg.file, file), + OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), + OPT_UINT("log-id", 'l', &cfg.log_id, log), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + goto ret; + } + + if (!wdc_enc_check_model(fd)) { + err = -EINVAL; + goto closed_fd; + } + + if (cfg.log_id > 0xff) { + fprintf(stderr, "Invalid log identifier: %d. Valid 0xd1, 0xd2, 0xd3, 0xd4, 0xe2, 0xe4\n", cfg.log_id); + goto closed_fd; + } + + if (cfg.xfer_size != 0) { + xfer_size = cfg.xfer_size; + if (!wdc_check_power_of_2(cfg.xfer_size)) { + fprintf(stderr, "%s: ERROR : xfer-size (%d) must be a power of 2\n", __func__, cfg.xfer_size); + err = -EINVAL; + goto closed_fd; + } + } + + /* Log IDs are only for specific enclosures */ + if (cfg.log_id) { + xfer_size = (xfer_size) ? xfer_size : WDC_NVME_ENC_LOG_SIZE_CHUNK; + len = cfg.file==NULL?0:strlen(cfg.file); + if (len > 0) { + output_fd = fopen(cfg.file,"wb"); + if (output_fd == 0) { + fprintf(stderr, "%s: ERROR : opening:%s : %s\n", __func__,cfg.file, strerror(errno)); + err = -EINVAL; + goto closed_fd; + } + } else { + output_fd = stdout; + } + if (cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_1 || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_2 + || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_3 || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_4) { + fprintf(stderr, "args - sz:%x logid:%x of:%s\n",xfer_size,cfg.log_id,cfg.file); + err = wdc_enc_get_nic_log(fd, cfg.log_id, xfer_size, WDC_NVME_ENC_NIC_LOG_SIZE, output_fd); + } else { + fprintf(stderr, "args - sz:%x logid:%x of:%s\n",xfer_size,cfg.log_id,cfg.file); + err = wdc_enc_submit_move_data(fd, NULL, 0, xfer_size, output_fd, cfg.log_id, 0, 0); + } + + if (err == WDC_RESULT_NOT_AVAILABLE) { + fprintf(stderr, "No Log/Crashdump available\n"); + err = 0; + } else if (err) { + fprintf(stderr, "ERROR:0x%x Failed to collect log-id:%x \n",err, cfg.log_id); + } + } +closed_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int wdc_enc_submit_move_data(int fd, char *cmd, int len, int xfer_size, FILE *out, int log_id, int cdw14, int cdw15) +{ + struct timespec time; + uint32_t response_size, more; + int err; + int handle; + uint32_t offset = 0; + char *buf; + + buf = (char *)malloc(sizeof(__u8) * xfer_size); + if (buf == NULL) { + fprintf(stderr, "%s: ERROR : malloc : %s\n", __func__, strerror(errno)); + return -1; + } + /* send something no matter what */ + cmd = (len) ? cmd : buf; + len = (len) ? len : 0x20; + + struct nvme_admin_cmd nvme_cmd = { + .opcode = WDC_NVME_ADMIN_ENC_MGMT_SND, + .nsid = 0, + .addr = (__u64)(uintptr_t) cmd, + .data_len = ((len + sizeof(uint32_t) - 1)/sizeof(uint32_t)) * sizeof(uint32_t), + .cdw10 = len, + .cdw12 = log_id, + .cdw13 = 0, + .cdw14 = cdw14, + .cdw15 = cdw15, + }; + + clock_gettime(CLOCK_REALTIME, &time); + srand(time.tv_nsec); + handle = random(); /* Handle to associate send request with receive request */ + nvme_cmd.cdw11 = handle; + +#ifdef WDC_NVME_CLI_DEBUG + unsigned char *d = (unsigned char*) nvme_cmd.addr; + unsigned char *md = (unsigned char*) nvme_cmd.metadata; + printf("NVME_ADMIN_COMMAND:\n" \ + "opcode: 0x%02x, flags: 0x%02x, rsvd: 0x%04x, nsid: 0x%08x, cdw2: 0x%08x, cdw3: 0x%08x, " \ + "metadata_len: 0x%08x, data_len: 0x%08x, cdw10: 0x%08x, cdw11: 0x%08x, cdw12: 0x%08x, " \ + "cdw13: 0x%08x, cdw14: 0x%08x, cdw15: 0x%08x, timeout_ms: 0x%08x, result: 0x%08x, " \ + "metadata: %s, " \ + "data: %s\n", \ + nvme_cmd.opcode, nvme_cmd.flags, nvme_cmd.rsvd1, nvme_cmd.nsid, nvme_cmd.cdw2, nvme_cmd.cdw3, \ + nvme_cmd.metadata_len, nvme_cmd.data_len, nvme_cmd.cdw10, nvme_cmd.cdw11, nvme_cmd.cdw12, \ + nvme_cmd.cdw13, nvme_cmd.cdw14, nvme_cmd.cdw15, nvme_cmd.timeout_ms, nvme_cmd.result, + md, \ + d); +#endif + nvme_cmd.result = 0; + err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd); + if (err == NVME_SC_INTERNAL) { + fprintf(stderr, "%s: WARNING : WDC : No log ID:x%x available\n", + __func__, log_id); + } + else if (err != 0) { + fprintf(stderr, "%s: ERROR : WDC : NVMe Snd Mgmt Status:%s(x%x)\n", + __func__, nvme_status_to_string(err), err ); + } else { + if (nvme_cmd.result == WDC_RESULT_NOT_AVAILABLE) + { + free(buf); + return WDC_RESULT_NOT_AVAILABLE; + } + + do { + /* Sent request, now go retrieve response */ + nvme_cmd.flags = 0; + nvme_cmd.opcode = WDC_NVME_ADMIN_ENC_MGMT_RCV; + nvme_cmd.addr = (__u64)(uintptr_t) buf; + nvme_cmd.data_len = xfer_size; + nvme_cmd.cdw10 = xfer_size / sizeof(uint32_t); + nvme_cmd.cdw11 = handle; + nvme_cmd.cdw12 = log_id; + nvme_cmd.cdw13 = offset / sizeof(uint32_t); + nvme_cmd.cdw14 = cdw14; + nvme_cmd.cdw15 = cdw15; + nvme_cmd.result = 0; /* returned result !=0 indicates more data available */ + err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd); + if (err != 0) { + more = 0; + fprintf(stderr, "%s: ERROR : WDC : NVMe Rcv Mgmt Status:%s(x%x)\n", + __func__, nvme_status_to_string(err), err); + } else { + more = nvme_cmd.result & WDC_RESULT_MORE_DATA; + response_size = nvme_cmd.result & ~WDC_RESULT_MORE_DATA; + fwrite(buf, response_size, 1, out); + offset += response_size; + if (more && (response_size & (sizeof(uint32_t)-1))) { + fprintf(stderr, "%s: ERROR : WDC : NVMe Rcv Mgmt response size:x%x not LW aligned\n", + __func__, response_size); + } + } + } while (more); + free(buf); + } + + return err; +} + +static int wdc_enc_get_nic_log(int fd, __u8 log_id, __u32 xfer_size, __u32 data_len, FILE *out) +{ + __u8 *dump_data; + __u32 curr_data_offset, curr_data_len; + int i, ret; + struct nvme_admin_cmd admin_cmd; + __u32 dump_length = data_len; + __u32 numd; + __u16 numdu, numdl; + + dump_data = (__u8 *) malloc(sizeof (__u8) * dump_length); + if (dump_data == NULL) { + fprintf(stderr, "%s: ERROR : malloc : %s\n",__func__, strerror(errno)); + return -1; + } + memset(dump_data, 0, sizeof (__u8) * dump_length); + memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd)); + curr_data_offset = 0; + curr_data_len = xfer_size; + i = 0; + + numd = (curr_data_len >> 2) - 1; + numdu = numd >> 16; + numdl = numd & 0xffff; + admin_cmd.opcode = nvme_admin_get_log_page; + admin_cmd.nsid = curr_data_offset; + admin_cmd.addr = (__u64)(uintptr_t) dump_data; + admin_cmd.data_len = curr_data_len; + admin_cmd.cdw10 = log_id | (numdl << 16); + admin_cmd.cdw11 = numdu; + while (curr_data_offset < data_len) { +#ifdef WDC_NVME_CLI_DEBUG + fprintf(stderr, "nsid 0x%08x addr 0x%08llx, data_len 0x%08x, cdw10 0x%08x, cdw11 0x%08x, cdw12 0x%08x, cdw13 0x%08x, cdw14 0x%08x \n", admin_cmd.nsid, admin_cmd.addr, admin_cmd.data_len, admin_cmd.cdw10, admin_cmd.cdw11, admin_cmd.cdw12, admin_cmd.cdw13, admin_cmd.cdw14); +#endif + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + if (ret !=0 ) { + fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n",__func__, nvme_status_to_string(ret), ret); + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%x, offset = 0x%x, addr = 0x%lx\n", + __func__, i, admin_cmd.data_len, curr_data_offset, (long unsigned int)admin_cmd.addr); + break; + } + + if ((curr_data_offset + xfer_size) <= data_len) + curr_data_len = xfer_size; + else + curr_data_len = data_len - curr_data_offset; /* last transfer */ + + curr_data_offset += curr_data_len; + numd = (curr_data_len >> 2) - 1; + numdu = numd >> 16; + numdl = numd & 0xffff; + admin_cmd.addr = (__u64)(uintptr_t)dump_data + (__u64)curr_data_offset; + admin_cmd.nsid = curr_data_offset; + admin_cmd.data_len = curr_data_len; + admin_cmd.cdw10 = log_id | (numdl << 16); + admin_cmd.cdw11 = numdu; + i++; + } + fwrite(dump_data, data_len, 1, out); + free(dump_data); + return ret; +} diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h index c6b2d3f..de6affa 100644 --- a/plugins/wdc/wdc-nvme.h +++ b/plugins/wdc/wdc-nvme.h @@ -25,11 +25,16 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions"), 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("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-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) ) ); diff --git a/plugins/wdc/wdc-utils.c b/plugins/wdc/wdc-utils.c index 80d9955..2740181 100644 --- a/plugins/wdc/wdc-utils.c +++ b/plugins/wdc/wdc-utils.c @@ -144,3 +144,18 @@ int wdc_UtilsStrCompare(char *pcSrc, char *pcDst) return *pcSrc - *pcDst; } +void wdc_StrFormat(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz) +{ + + fmt_sz = snprintf(formatter,fmt_sz, "%-*.*s", + (int)tofmtsz, (int)tofmtsz, tofmt); + /* trim() the obnoxious trailing white lines */ + while (fmt_sz) { + if (formatter[fmt_sz - 1] != ' ' && formatter[fmt_sz - 1] != '\0') { + formatter[fmt_sz] = '\0'; + break; + } + fmt_sz--; + } +} + diff --git a/plugins/wdc/wdc-utils.h b/plugins/wdc/wdc-utils.h index 251339f..04d4b83 100644 --- a/plugins/wdc/wdc-utils.h +++ b/plugins/wdc/wdc-utils.h @@ -74,4 +74,5 @@ int wdc_UtilsGetTime(PUtilsTimeInfo timeInfo); 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); diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c new file mode 100644 index 0000000..91e52be --- /dev/null +++ b/plugins/ymtc/ymtc-nvme.c @@ -0,0 +1,148 @@ +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "linux/nvme_ioctl.h" + +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "plugin.h" + +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include "ymtc-nvme.h" +#include "ymtc-utils.h" + +static void get_ymtc_smart_info(struct nvme_ymtc_smart_log *smart, int index, u8 *nm_val, u8 *raw_val) +{ + memcpy(nm_val, smart->itemArr[index].nmVal, NM_SIZE); + memcpy(raw_val, smart->itemArr[index].rawVal, RAW_SIZE); +} + +static int show_ymtc_smart_log(int fd, __u32 nsid, const char *devname, + struct nvme_ymtc_smart_log *smart) +{ + struct nvme_id_ctrl ctrl; + char fw_ver[10]; + int err = 0; + + u8 *nm = malloc(NM_SIZE * sizeof(u8)); + u8 *raw = malloc(RAW_SIZE * sizeof(u8)); + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + return err; + + snprintf(fw_ver, sizeof(fw_ver), "%c.%c%c.%c%c%c%c", + ctrl.fr[0], ctrl.fr[1], ctrl.fr[2], ctrl.fr[3], + ctrl.fr[4], ctrl.fr[5], ctrl.fr[6]); + + /* Table Title */ + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid); + /* Clumn Name*/ + printf("key normalized raw\n"); + /* 00 SI_VD_PROGRAM_FAIL */ + get_ymtc_smart_info(smart, SI_VD_PROGRAM_FAIL, nm, raw); + printf("program_fail_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 01 SI_VD_ERASE_FAIL */ + get_ymtc_smart_info(smart, SI_VD_ERASE_FAIL, nm, raw); + printf("erase_fail_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 02 SI_VD_WEARLEVELING_COUNT */ + get_ymtc_smart_info(smart, SI_VD_WEARLEVELING_COUNT, nm, raw); + printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", *nm, + *raw, *(raw+2), *(raw+4)); + /* 03 SI_VD_E2E_DECTECTION_COUNT */ + get_ymtc_smart_info(smart, SI_VD_E2E_DECTECTION_COUNT, nm, raw); + printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 04 SI_VD_PCIE_CRC_ERR_COUNT */ + get_ymtc_smart_info(smart, SI_VD_PCIE_CRC_ERR_COUNT, nm, raw); + printf("crc_error_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 08 SI_VD_THERMAL_THROTTLE_STATUS */ + get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_STATUS, nm, raw); + printf("thermal_throttle_status : %3d%% %"PRIu64"%%, cnt: %"PRIu64"\n", *nm, + int48_to_long(raw), int48_to_long(raw+1)); + /* 11 SI_VD_TOTAL_WRITE */ + get_ymtc_smart_info(smart, SI_VD_TOTAL_WRITE, nm, raw); + printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 12 SI_VD_HOST_WRITE */ + get_ymtc_smart_info(smart, SI_VD_HOST_WRITE, nm, raw); + printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 14 SI_VD_TOTAL_READ */ + get_ymtc_smart_info(smart, SI_VD_TOTAL_READ, nm, raw); + printf("nand_bytes_read : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 15 SI_VD_TEMPT_SINCE_BORN */ + get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BORN, nm, raw); + printf("tempt_since_born : %3d%% max: %u, min: %u, curr: %u\n", *nm, + *raw, *(raw+2), *(raw+4)); + /* 16 SI_VD_POWER_CONSUMPTION */ + get_ymtc_smart_info(smart, SI_VD_POWER_CONSUMPTION, nm, raw); + printf("power_consumption : %3d%% max: %u, min: %u, curr: %u\n", *nm, + *raw, *(raw+2), *(raw+4)); + /* 17 SI_VD_TEMPT_SINCE_BOOTUP */ + get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BOOTUP, nm, raw); + printf("tempt_since_bootup : %3d%% max: %u, min: %u, curr: %u\n", *nm, *raw, + *(raw+2), *(raw+4)); + /* 18 SI_VD_POWER_LOSS_PROTECTION */ + get_ymtc_smart_info(smart, SI_VD_POWER_LOSS_PROTECTION, nm, raw); + printf("power_loss_protection : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 19 SI_VD_READ_FAIL */ + get_ymtc_smart_info(smart, SI_VD_READ_FAIL, nm, raw); + printf("read_fail : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 20 SI_VD_THERMAL_THROTTLE_TIME */ + get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_TIME, nm, raw); + printf("thermal_throttle_time : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 21 SI_VD_FLASH_MEDIA_ERROR */ + get_ymtc_smart_info(smart, SI_VD_FLASH_MEDIA_ERROR, nm, raw); + printf("flash_error_media_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + + free(nm); + free(raw); + + return err; +} + +static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + struct nvme_ymtc_smart_log smart_log; + int err, fd; + char *desc = "Get Ymtc vendor specific additional smart log (optionally, "\ + "for the specified namespace), and show it."; + const char *namespace = "(optional) desired namespace"; + const char *raw = "dump output in binary format"; + struct config { + __u32 namespace_id; + int raw_binary; + }; + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); + if (!err) { + if (!cfg.raw_binary) + err = show_ymtc_smart_log(fd, cfg.namespace_id, devicename, &smart_log); + else + d_raw((unsigned char *)&smart_log, sizeof(smart_log)); + } + if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + + return err; +} diff --git a/plugins/ymtc/ymtc-nvme.h b/plugins/ymtc/ymtc-nvme.h new file mode 100644 index 0000000..739fd37 --- /dev/null +++ b/plugins/ymtc/ymtc-nvme.h @@ -0,0 +1,24 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/ymtc/ymtc-nvme + +#if !defined(YMTC_NVME) || defined(CMD_HEADER_MULTI_READ) +#define YMTC_NVME + +#include "cmd.h" +#include "common.h" + +#include <ctype.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +PLUGIN(NAME("ymtc", "Ymtc vendor specific extensions"), + COMMAND_LIST( + ENTRY("smart-log-add", "Retrieve Ymtc SMART Log, show it", get_additional_smart_log) + ) +); + +#endif + +#include "define_cmd.h" + diff --git a/plugins/ymtc/ymtc-utils.h b/plugins/ymtc/ymtc-utils.h new file mode 100644 index 0000000..700c764 --- /dev/null +++ b/plugins/ymtc/ymtc-utils.h @@ -0,0 +1,80 @@ +#ifndef __YMTC_UTILS_H__ +#define __YMTC_UTILS_H__ + +#define SMART_INFO_SIZE 4096 + +#define ID_SIZE 3 +#define NM_SIZE 2 +#define RAW_SIZE 7 + +typedef unsigned char u8; + +/* Additional smart external ID */ +#define SI_VD_PROGRAM_FAIL_ID 0xAB +#define SI_VD_ERASE_FAIL_ID 0xAC +#define SI_VD_WEARLEVELING_COUNT_ID 0xAD +#define SI_VD_E2E_DECTECTION_COUNT_ID 0xB8 +#define SI_VD_PCIE_CRC_ERR_COUNT_ID 0xC7 +#define SI_VD_TIMED_WORKLOAD_MEDIA_WEAR_ID 0xE2 +#define SI_VD_TIMED_WORKLOAD_HOST_READ_ID 0xE3 +#define SI_VD_TIMED_WORKLOAD_TIMER_ID 0xE4 +#define SI_VD_THERMAL_THROTTLE_STATUS_ID 0xEA +#define SI_VD_RETRY_BUFF_OVERFLOW_COUNT_ID 0xF0 +#define SI_VD_PLL_LOCK_LOSS_COUNT_ID 0xF3 +#define SI_VD_TOTAL_WRITE_ID 0xF4 +#define SI_VD_HOST_WRITE_ID 0xF5 +#define SI_VD_SYSTEM_AREA_LIFE_LEFT_ID 0xF6 +#define SI_VD_TOTAL_READ_ID 0xFA +#define SI_VD_TEMPT_SINCE_BORN_ID 0xE7 +#define SI_VD_POWER_CONSUMPTION_ID 0xE8 +#define SI_VD_TEMPT_SINCE_BOOTUP_ID 0xAF +#define SI_VD_POWER_LOSS_PROTECTION_ID 0xEC +#define SI_VD_READ_FAIL_ID 0xF2 +#define SI_VD_THERMAL_THROTTLE_TIME_ID 0xEB +#define SI_VD_FLASH_MEDIA_ERROR_ID 0xED + +/* Addtional smart internal ID */ +typedef enum +{ + /* smart attr following intel */ + SI_VD_PROGRAM_FAIL = 0, /* 0xAB */ + SI_VD_ERASE_FAIL = 1, /* 0xAC */ + SI_VD_WEARLEVELING_COUNT = 2,/* 0xAD */ + SI_VD_E2E_DECTECTION_COUNT = 3, /* 0xB8 */ + SI_VD_PCIE_CRC_ERR_COUNT = 4, /* 0xC7, 2 port data in one attribute */ + SI_VD_THERMAL_THROTTLE_STATUS = 8, /* 0xEA */ + SI_VD_TOTAL_WRITE = 11, /* 0xF4, unit is 32MiB */ + SI_VD_HOST_WRITE = 12, /* 0xF5, unit is 32MiB */ + SI_VD_TOTAL_READ = 14, /* 0xFA, unit is 32MiB */ + + /* smart attr self defined */ + SI_VD_TEMPT_SINCE_BORN = 15, /* 0xE7 */ + SI_VD_POWER_CONSUMPTION = 16, /* 0xE8 */ + SI_VD_TEMPT_SINCE_BOOTUP = 17, /* 0xAF */ + SI_VD_POWER_LOSS_PROTECTION = 18, /* 0xEC */ + SI_VD_READ_FAIL = 19, /* 0xF2 */ + SI_VD_THERMAL_THROTTLE_TIME = 20, /* 0xEB */ + SI_VD_FLASH_MEDIA_ERROR = 21, /* 0xED */ + NR_SMART_ITEMS, +} si_vendor_smart_item_e; + +// Intel Format +struct nvme_ymtc_smart_log_item +{ + /* Item identifier */ + u8 id[ID_SIZE]; + /* Normalized value or percentage. In the range from 0 to 100. */ + u8 nmVal[NM_SIZE]; + /* raw value */ + u8 rawVal[RAW_SIZE]; +}; + +struct nvme_ymtc_smart_log +{ + struct nvme_ymtc_smart_log_item itemArr[NR_SMART_ITEMS]; + + u8 resv[SMART_INFO_SIZE - sizeof(struct nvme_ymtc_smart_log_item) * NR_SMART_ITEMS]; +}; + +#endif // __YMTC_UTILS_H__ + diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c new file mode 100644 index 0000000..7f6f52e --- /dev/null +++ b/plugins/zns/zns.c @@ -0,0 +1,923 @@ +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <linux/fs.h> +#include <sys/stat.h> + +#include "nvme.h" +#include "nvme-ioctl.h" +#include "nvme-print.h" +#include "nvme-status.h" + +#define CREATE_CMD +#include "zns.h" + +static const char *namespace_id = "Namespace identifier to use"; + +static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an ZNS specific Identify Controller command to "\ + "the given device and report information about the specified "\ + "controller in various formats."; + + enum nvme_print_flags flags; + struct nvme_zns_id_ctrl ctrl; + int fd, err = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_zns_identify_ctrl(fd, &ctrl); + if (!err) + nvme_show_zns_id_ctrl(&ctrl, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns identify controller"); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an ZNS specific Identify Namespace command to "\ + "the given device and report information about the specified "\ + "namespace in varios formats."; + const char *vendor_specific = "dump binary vendor fields"; + const char *human_readable = "show identify in readable format"; + + enum nvme_print_flags flags; + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + int fd, err = -1; + + struct config { + char *output_format; + __u32 namespace_id; + int human_readable; + int vendor_specific; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FLAG("vendor-specific", 'v', &cfg.vendor_specific, vendor_specific), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.vendor_specific) + flags |= VS; + if (cfg.human_readable) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_identify_ns(fd, cfg.namespace_id, false, &id_ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + err = nvme_zns_identify_ns(fd, cfg.namespace_id, &ns); + if (!err) + nvme_show_zns_id_ns(&ns, &id_ns, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns identify namespace"); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int __zns_mgmt_send(int fd, __u32 namespace_id, __u64 zslba, + bool select_all, enum nvme_zns_send_action zsa, __u32 data_len, void *buf) +{ + int err; + + err = nvme_zns_mgmt_send(fd, namespace_id, zslba, select_all, zsa, + data_len, buf); + close(fd); + return err; +} + +static int zns_mgmt_send(int argc, char **argv, struct command *cmd, struct plugin *plugin, + const char *desc, enum nvme_zns_send_action zsa) +{ + const char *zslba = "starting LBA of the zone for this command"; + const char *select_all = "send command to all zones"; + + int err, fd; + char *command; + + struct config { + __u64 zslba; + __u32 namespace_id; + bool select_all; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + err = asprintf(&command, "%s-%s", plugin->name, cmd->name); + if (err < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto free; + } + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, + cfg.select_all, zsa, 0, NULL); + if (!err) + printf("%s: Success, action:%d zone:%"PRIx64" all:%d nsid:%d\n", + command, zsa, (uint64_t)cfg.zslba, (int)cfg.select_all, + cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror(desc); +free: + free(command); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int get_zdes_bytes(int fd, __u32 nsid) +{ + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + __u8 lbaf; + int err; + + err = nvme_identify_ns(fd, nsid, false, &id_ns); + if (err > 0){ + nvme_show_status(err); + return err; + } + else if (err < 0){ + perror("identify namespace"); + return err; + } + + err = nvme_zns_identify_ns(fd, nsid, &ns); + if (err > 0){ + nvme_show_status(err); + return err; + } + else if (err < 0){ + perror("zns identify namespace"); + return err; + } + + lbaf = id_ns.flbas & NVME_NS_FLBAS_LBA_MASK; + return ns.lbafe[lbaf].zdes << 6; +} + +static int zone_mgmt_send(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Zone Management Send"; + const char *zslba = "starting LBA of the zone for this command"; + const char *select_all = "send command to all zones"; + const char *zsa = "zone send action"; + const char *data_len = "buffer length if data required"; + const char *data = "optional file for data (default stdin)"; + + int fd, ffd = STDIN_FILENO, err = -1; + void *buf = NULL; + + struct config { + __u64 zslba; + __u32 namespace_id; + bool select_all; + __u8 zsa; + int data_len; + char *file; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_BYTE("zsa", 'z', &cfg.zsa, zsa), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (!cfg.zsa) { + fprintf(stderr, "zone send action must be specified\n"); + err = -EINVAL; + goto close_fd; + } + + if (cfg.zsa == NVME_ZNS_ZSA_SET_DESC_EXT) { + if(!cfg.data_len) { + cfg.data_len = get_zdes_bytes(fd, cfg.namespace_id); + if (cfg.data_len == 0) { + fprintf(stderr, + "Zone Descriptor Extensions are not supported\n"); + goto close_fd; + } else if (cfg.data_len < 0) { + err = cfg.data_len; + goto close_fd; + } + } + if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { + fprintf(stderr, "can not allocate feature payload\n"); + goto close_fd; + } + memset(buf, 0, cfg.data_len); + + if (cfg.file) { + ffd = open(cfg.file, O_RDONLY); + if (ffd < 0) { + perror(cfg.file); + goto free; + } + } + + err = read(ffd, (void *)buf, cfg.data_len); + if (err < 0) { + perror("read"); + goto close_ffd; + } + } else { + if (cfg.file || cfg.data_len) { + fprintf(stderr, + "data, data_len only valid with set extended descriptor\n"); + err = -EINVAL; + goto close_fd; + } + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, cfg.select_all, + cfg.zsa, cfg.data_len, buf); + if (!err) + printf("zone-mgmt-send: Success, action:%d zone:%"PRIx64" " + "all:%d nsid:%d\n", + cfg.zsa, (uint64_t)cfg.zslba, (int)cfg.select_all, + cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-mgmt-send"); + +close_ffd: + if (cfg.file) + close(ffd); +free: + free(buf); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int close_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Close zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_CLOSE); +} + +static int finish_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Finish zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_FINISH); +} + +static int open_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Open zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_OPEN); +} + +static int reset_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Reset zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_RESET); +} + +static int offline_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Offline zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_OFFLINE); +} + +static int set_zone_desc(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Set Zone Descriptor Extension\n"; + const char *zslba = "starting LBA of the zone for this command"; + const char *data = "optional file for zone extention data (default stdin)"; + + int fd, ffd = STDIN_FILENO, err; + void *buf = NULL; + __u32 data_len; + + struct config { + __u64 zslba; + __u32 namespace_id; + char *file; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + data_len = get_zdes_bytes(fd, cfg.namespace_id); + + if (!data_len) { + fprintf(stderr, + "zone format does not provide descriptor extention\n"); + errno = EINVAL; + err = -1; + goto close_fd; + } + + buf = calloc(1, data_len); + if (!buf) { + perror("could not alloc memory for zone desc"); + err = -ENOMEM; + goto close_fd; + } + + if (cfg.file) { + ffd = open(cfg.file, O_RDONLY); + if (ffd < 0) { + perror(cfg.file); + err = -1; + goto free; + } + } + + err = read(ffd, (void *)buf, data_len); + if (err < 0) { + perror("read"); + goto close_ffd; + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, 0, + NVME_ZNS_ZSA_SET_DESC_EXT, data_len, buf); + if (!err) + printf("set-zone-desc: Success, zone:%"PRIx64" nsid:%d\n", + (uint64_t)cfg.zslba, cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns set-zone-desc"); +close_ffd: + if (cfg.file) + close(ffd); +free: + free(buf); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int zone_mgmt_recv(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Zone Management Receive"; + const char *zslba = "starting LBA of the zone"; + const char *zra = "Zone Receive Action"; + const char *zrasf = "Zone Receive Action Specific Field(Reporting Options)"; + const char *partial = "Zone Receive Action Specific Features(Partial Report)"; + const char *data_len = "length of data in bytes"; + + enum nvme_print_flags flags; + int fd, err = -1; + void *data = NULL; + + struct config { + char *output_format; + __u64 zslba; + __u32 namespace_id; + __u8 zra; + __u8 zrasf; + bool partial; + __u32 data_len; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_BYTE("zra", 'z', &cfg.zra, zra), + OPT_BYTE("zrasf", 'S', &cfg.zrasf, zrasf), + OPT_FLAG("partial", 'p', &cfg.partial, partial), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (cfg.zra == NVME_ZNS_ZRA_REPORT_ZONES && !cfg.data_len) { + fprintf(stderr, "error: data len is needed for NVME_ZRA_ZONE_REPORT\n"); + err = -EINVAL; + goto close_fd; + } + if (cfg.data_len) { + data = calloc(1, cfg.data_len); + if (!data) { + perror("could not alloc memory for zone mgmt receive data"); + err = -ENOMEM; + goto close_fd; + } + } + + err = nvme_zns_mgmt_recv(fd, cfg.namespace_id, cfg.zslba, cfg.zra, + cfg.zrasf, cfg.partial, cfg.data_len, data); + if (!err) + printf("zone-mgmt-recv: Success, action:%d zone:%"PRIx64" nsid:%d\n", + cfg.zra, (uint64_t)cfg.zslba, cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-mgmt-recv"); + + free(data); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int report_zones(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve the Report Zones data structure"; + const char *zslba = "starting LBA of the zone"; + const char *num_descs = "number of descriptors to retrieve (default: all of them)"; + const char *state = "state of zones to list"; + const char *ext = "set to use the extended report zones"; + const char *part = "set to use the partial report"; + const char *human_readable = "show report zones in readable format"; + + enum nvme_print_flags flags; + int fd, zdes = 0, err = -1; + __u32 report_size; + void *report; + bool huge = false; + + struct config { + char *output_format; + __u64 zslba; + __u32 namespace_id; + int num_descs; + int state; + int human_readable; + bool extended; + bool partial; + }; + + struct config cfg = { + .output_format = "normal", + .num_descs = -1, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_UINT("descs", 'd', &cfg.num_descs, num_descs), + OPT_UINT("state", 'S', &cfg.state, state), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable), + OPT_FLAG("extended", 'e', &cfg.extended, ext), + OPT_FLAG("partial", 'p', &cfg.partial, part), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.human_readable) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (cfg.extended) { + zdes = get_zdes_bytes(fd, cfg.namespace_id); + if (zdes < 0) { + err = zdes; + goto close_fd; + } + } + + if (cfg.num_descs == -1) { + struct nvme_zone_report r; + + err = nvme_zns_report_zones(fd, cfg.namespace_id, 0, + 0, cfg.state, 0, sizeof(r), &r); + if (err > 0) { + nvme_show_status(err); + goto close_fd; + } else if (err < 0) { + perror("zns report-zones"); + goto close_fd; + } + cfg.num_descs = le64_to_cpu(r.nr_zones); + } + + report_size = sizeof(struct nvme_zone_report) + cfg.num_descs * + (sizeof(struct nvme_zns_desc) + cfg.num_descs * zdes); + + report = nvme_alloc(report_size, &huge); + if (!report) { + perror("alloc"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_zns_report_zones(fd, cfg.namespace_id, cfg.zslba, + cfg.extended, cfg.state, cfg.partial, report_size, report); + if (!err) + nvme_show_zns_report_zones(report, cfg.num_descs, zdes, + report_size, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns report-zones"); + + nvme_free(report, huge); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int zone_append(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "The zone append command is used to write to a zone "\ + "using the slba of the zone, and the write will be appended from the "\ + "write pointer of the zone"; + const char *zslba = "starting LBA of the zone"; + const char *data = "file containing data to write"; + const char *metadata = "file with metadata to be written"; + const char *limited_retry = "limit media access attempts"; + const char *fua = "force unit access"; + const char *prinfo = "protection information action and checks field"; + const char *piremap = "protection information remap (for type 1 PI)"; + const char *ref_tag = "reference tag (for end to end PI)"; + const char *lbat = "logical block application tag (for end to end PI)"; + const char *lbatm = "logical block application tag mask (for end to end PI)"; + const char *metadata_size = "size of metadata in bytes"; + const char *data_size = "size of data in bytes"; + const char *latency = "output latency statistics"; + + int err = -1, fd, dfd = STDIN_FILENO, mfd = STDIN_FILENO; + unsigned int lba_size, meta_size; + void *buf = NULL, *mbuf = NULL; + __u16 nblocks, control = 0; + __u64 result; + struct timeval start_time, end_time; + + struct nvme_id_ns ns; + + struct config { + char *data; + char *metadata; + __u64 zslba; + __u64 data_size; + __u64 metadata_size; + int limited_retry; + int fua; + __u32 namespace_id; + __u32 ref_tag; + __u16 lbat; + __u16 lbatm; + __u8 prinfo; + int piremap; + int latency; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("zslba", 's', &cfg.zslba, zslba), + OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), + OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size), + OPT_FILE("data", 'd', &cfg.data, data), + OPT_FILE("metadata", 'M', &cfg.metadata, metadata), + OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), + OPT_FLAG("force-unit-access", 'f', &cfg.fua, fua), + OPT_UINT("ref-tag", 'r', &cfg.ref_tag, ref_tag), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, lbatm), + OPT_SHRT("app-tag", 'a', &cfg.lbat, lbat), + OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), + OPT_FLAG("piremap", 'P', &cfg.piremap, piremap), + OPT_FLAG("latency", 't', &cfg.latency, latency), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (!cfg.data_size) { + fprintf(stderr, "Append size not provided\n"); + errno = EINVAL; + goto close_fd; + } + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_identify_ns(fd, cfg.namespace_id, false, &ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + lba_size = 1 << ns.lbaf[(ns.flbas & 0x0f)].ds; + if (cfg.data_size & (lba_size - 1)) { + fprintf(stderr, + "Data size:%#"PRIx64" not aligned to lba size:%#x\n", + (uint64_t)cfg.data_size, lba_size); + errno = EINVAL; + goto close_ns; + } + + meta_size = ns.lbaf[(ns.flbas & 0x0f)].ms; + if (meta_size && !(meta_size == 8 && (cfg.prinfo & 0x8)) && + (!cfg.metadata_size || cfg.metadata_size % meta_size)) { + fprintf(stderr, + "Metadata size:%#"PRIx64" not aligned to metadata size:%#x\n", + (uint64_t)cfg.metadata_size, meta_size); + errno = EINVAL; + goto close_ns; + } + + if (cfg.prinfo > 0xf) { + fprintf(stderr, "Invalid value for prinfo:%#x\n", cfg.prinfo); + errno = EINVAL; + goto close_ns; + } + + if (cfg.data) { + dfd = open(cfg.data, O_RDONLY); + if (dfd < 0) { + perror(cfg.data); + goto close_ns; + } + } + + if (posix_memalign(&buf, getpagesize(), cfg.data_size)) { + fprintf(stderr, "No memory for data size:%"PRIx64"\n", + (uint64_t)cfg.data_size); + goto close_dfd; + } + + memset(buf, 0, cfg.data_size); + err = read(dfd, buf, cfg.data_size); + if (err < 0) { + perror("read-data"); + goto free_data; + } + + if (cfg.metadata) { + mfd = open(cfg.metadata, O_RDONLY); + if (mfd < 0) { + perror(cfg.metadata); + err = -1; + goto close_dfd; + } + } + + if (cfg.metadata_size) { + if (posix_memalign(&mbuf, getpagesize(), meta_size)) { + fprintf(stderr, "No memory for metadata size:%d\n", + meta_size); + err = -1; + goto close_mfd; + } + + memset(mbuf, 0, cfg.metadata_size); + err = read(mfd, mbuf, cfg.metadata_size); + if (err < 0) { + perror("read-metadata"); + goto free_meta; + } + } + + nblocks = (cfg.data_size / lba_size) - 1; + control |= (cfg.prinfo << 10); + if (cfg.limited_retry) + control |= NVME_RW_LR; + if (cfg.fua) + control |= NVME_RW_FUA; + if (cfg.piremap) + control |= NVME_RW_PIREMAP; + + gettimeofday(&start_time, NULL); + err = nvme_zns_append(fd, cfg.namespace_id, cfg.zslba, nblocks, + control, cfg.ref_tag, cfg.lbat, cfg.lbatm, + cfg.data_size, buf, cfg.metadata_size, mbuf, + &result); + gettimeofday(&end_time, NULL); + if (cfg.latency) + printf(" latency: zone append: %llu us\n", + elapsed_utime(start_time, end_time)); + + if (!err) + printf("Success appended data to LBA %"PRIx64"\n", (uint64_t)result); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-append"); + +free_meta: + free(mbuf); +close_mfd: + if (cfg.metadata) + close(mfd); +free_data: + free(buf); +close_dfd: + if (cfg.data) + close(dfd); +close_ns: +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int changed_zone_list(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Changed Zone log for the given device"; + const char *rae = "retain an asynchronous event"; + + struct nvme_zns_changed_zone_log log; + enum nvme_print_flags flags; + int fd, err = -1; + + struct config { + char *output_format; + __u32 namespace_id; + bool rae; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_get_log(fd, cfg.namespace_id, NVME_LOG_ZONE_CHANGED_LIST, + cfg.rae, NVME_NO_LOG_LSP, sizeof(log), &log); + if (!err) + nvme_show_zns_changed(&log, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns changed-zone-list"); + +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} diff --git a/plugins/zns/zns.h b/plugins/zns/zns.h new file mode 100644 index 0000000..a92de69 --- /dev/null +++ b/plugins/zns/zns.h @@ -0,0 +1,30 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/zns/zns + +#if !defined(ZNS_NVME) || defined(CMD_HEADER_MULTI_READ) +#define ZNS_NVME + +#include "cmd.h" + +PLUGIN(NAME("zns", "Zoned Namespace Command Set"), + COMMAND_LIST( + ENTRY("id-ctrl", "Retrieve ZNS controller identification", id_ctrl) + ENTRY("id-ns", "Retrieve ZNS namespace identification", id_ns) + ENTRY("zone-mgmt-recv", "Sends the zone management receive command", zone_mgmt_recv) + ENTRY("zone-mgmt-send", "Sends the zone management send command", zone_mgmt_send) + ENTRY("report-zones", "Retrieve the Report Zones report", report_zones) + ENTRY("close-zone", "Closes one or more zones", close_zone) + ENTRY("finish-zone", "Finishes one or more zones", finish_zone) + ENTRY("open-zone", "Opens one or more zones", open_zone) + ENTRY("reset-zone", "Resets one or more zones", reset_zone) + ENTRY("offline-zone", "Offlines one or more zones", offline_zone) + ENTRY("set-zone-desc", "Attaches zone descriptor extension data", set_zone_desc) + ENTRY("zone-append", "Writes data and metadata (if applicable), appended to the end of the requested zone", zone_append) + ENTRY("changed-zone-list", "Retrieves the changed zone list log", changed_zone_list) + ) +); + +#endif + +#include "define_cmd.h" + |