summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plugins/amzn/amzn-nvme.c59
-rw-r--r--plugins/amzn/amzn-nvme.h17
-rw-r--r--plugins/dera/dera-nvme.c3
-rw-r--r--plugins/huawei/huawei-nvme.c3
-rw-r--r--plugins/intel/intel-nvme.c513
-rw-r--r--plugins/intel/intel-nvme.h1
-rw-r--r--plugins/memblaze/memblaze-nvme.c781
-rw-r--r--plugins/memblaze/memblaze-nvme.h13
-rw-r--r--plugins/memblaze/memblaze-utils.h63
-rw-r--r--plugins/micron/micron-nvme.c3293
-rw-r--r--plugins/micron/micron-nvme.h10
-rw-r--r--plugins/netapp/netapp-nvme.c13
-rw-r--r--plugins/nvidia/nvidia-nvme.c58
-rw-r--r--plugins/nvidia/nvidia-nvme.h17
-rw-r--r--plugins/scaleflux/sfx-nvme.c377
-rw-r--r--plugins/scaleflux/sfx-nvme.h4
-rw-r--r--plugins/seagate/seagate-nvme.c33
-rw-r--r--plugins/shannon/shannon-nvme.c7
-rw-r--r--plugins/toshiba/toshiba-nvme.c2
-rw-r--r--plugins/virtium/virtium-nvme.c6
-rw-r--r--plugins/wdc/wdc-nvme.c3187
-rw-r--r--plugins/wdc/wdc-nvme.h5
-rw-r--r--plugins/wdc/wdc-utils.c15
-rw-r--r--plugins/wdc/wdc-utils.h1
-rw-r--r--plugins/ymtc/ymtc-nvme.c148
-rw-r--r--plugins/ymtc/ymtc-nvme.h24
-rw-r--r--plugins/ymtc/ymtc-utils.h80
-rw-r--r--plugins/zns/zns.c923
-rw-r--r--plugins/zns/zns.h30
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, &param1, &param2)) {
+ 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"
+